在Swift上从URL加载/下载图像


412

我想从我的应用程序中的URL加载图像,因此我首先尝试使用Objective-C,但是它可以正常工作,但是对于Swift,我遇到了编译错误:

'imageWithData'不可用:使用对象构造'UIImage(data :)'

我的功能:

@IBOutlet var imageView : UIImageView

override func viewDidLoad() {
    super.viewDidLoad()

    var url:NSURL = NSURL.URLWithString("http://myURL/ios8.png")
    var data:NSData = NSData.dataWithContentsOfURL(url, options: nil, error: nil)

    imageView.image = UIImage.imageWithData(data)// Error here
}

在Objective-C中:

- (void)viewDidLoad {
    [super viewDidLoad];

    NSURL *url = [NSURL URLWithString:(@"http://myURL/ios8.png")];
    NSData *data = [NSData dataWithContentsOfURL:url];

    _imageView.image = [UIImage imageWithData: data];
    _labelURL.text = @"http://www.quentinroussat.fr/assets/img/iOS%20icon's%20Style/ios8.png";
 }

有人可以解释一下为什么imageWithData:Swift无法使用它,以及如何解决该问题。


3
试试这个imageURL.image = UIImage(data: myDataVar)
aleclarson

完美的工作!谢谢但是我不为什么目标C这种方法的工作,而不是在斯威夫特......奇怪
QuentR

如果您在使用Cocoa类时遇到问题,请尝试CMD +单击类名称,您应该能够看到该类的Swift界面!
aleclarson

3
如果让url = NSURL(string:“ imageurl”){如果让数据= NSData(contentsOfURL:url){imageView.image = UIImage(data:data)}}
Hardik Bar 2015年

2
@Leo Dabus这个问题不是Swift 2特有的。请停止添加该标签。
Mick MacCallum

Answers:


769

Xcode 8或更高版本•Swift 3或更高版本

同步:

if let filePath = Bundle.main.path(forResource: "imageName", ofType: "jpg"), let image = UIImage(contentsOfFile: filePath) {
    imageView.contentMode = .scaleAspectFit
    imageView.image = image
}

异步地:

创建带有完成处理程序的方法以从您的网址获取图像数据

func getData(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ()) {
    URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}

创建下载图像的方法(启动任务)

func downloadImage(from url: URL) {
    print("Download Started")
    getData(from: url) { data, response, error in
        guard let data = data, error == nil else { return }
        print(response?.suggestedFilename ?? url.lastPathComponent)
        print("Download Finished")
        DispatchQueue.main.async() { [weak self] in
            self?.imageView.image = UIImage(data: data)
        }
    }
}

用法:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    print("Begin of code")
    let url = URL(string: "https://cdn.arstechnica.net/wp-content/uploads/2018/06/macOS-Mojave-Dynamic-Wallpaper-transition.jpg")! 
    downloadImage(from: url)
    print("End of code. The image will continue downloading in the background and it will be loaded when it ends.")
}

扩展名

extension UIImageView {
    func downloaded(from url: URL, contentMode mode: UIViewContentMode = .scaleAspectFit) {  // for swift 4.2 syntax just use ===> mode: UIView.ContentMode
        contentMode = mode
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard
                let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
                let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
                let data = data, error == nil,
                let image = UIImage(data: data)
                else { return }
            DispatchQueue.main.async() { [weak self] in
                self?.image = image
            }
        }.resume()
    }
    func downloaded(from link: String, contentMode mode: UIViewContentMode = .scaleAspectFit) {  // for swift 4.2 syntax just use ===> mode: UIView.ContentMode
        guard let url = URL(string: link) else { return }
        downloaded(from: url, contentMode: mode)
    }
}

用法:

imageView.downloaded(from: "https://cdn.arstechnica.net/wp-content/uploads/2018/06/macOS-Mojave-Dynamic-Wallpaper-transition.jpg")

13
在这里只是一个注意事项,您应该为其设置关联的对象;否则,您可能会在彼此之间加载图像。例如,UITableView在一个单元格显示图像并返回出队的单元格时更新图像的。如果进程#1花费的时间更长,则进程#2将显示进程2的图像,然后进程1将对其进行更新,即使该图像对用户而言不再有效。
Paul Peelen

1
这解决了我的问题。当我尝试在表格视图中加载带有url的图像时,滚动到顶部或底部时滚动视图实际上没有响应。我使用了UIImageView Extension,它解决了我的问题。谢谢。
JengGe Chao

1
@LeoDabus好的,谢谢。感谢您回答如此多的问题!您就像是Swift和iOS的教授。:)
Crashalot '16

2
@LeoDabus,您为什么使用dataTaskWithURL而不是downloadTaskWithURL?
Crashalot '16

3
第一个下载到内存中,第二个下载到文件中
Leo Dabus

349

(快速更新4) 要直接回答原始问题,以下是发布的Objective-C代码片段的快速等效内容。

let url = URL(string: image.url)
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
imageView.image = UIImage(data: data!)

免责声明:

重要的是要注意,该Data(contentsOf:)方法将在执行代码的同一线程中同步下载url的内容,因此请不要在应用程序的主线程中调用此方法。

使同一代码异步运行而不阻塞UI的一种简单方法是使用GCD:

let url = URL(string: image.url)

DispatchQueue.global().async {
    let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
    DispatchQueue.main.async {
        imageView.image = UIImage(data: data!)
    }
}

就是说,在现实生活中的应用程序中,如果要获得最佳的用户体验并避免多次下载同一图像,则可能不仅要下载它们,还要对其进行缓存。已经有很多库可以做到非常无缝,它们都非常易于使用。我个人推荐翠鸟

import Kingfisher

let url = URL(string: "url_of_your_image")
// this downloads the image asynchronously if it's not cached yet
imageView.kf.setImage(with: url) 

就是这样


12
“轻松”是主观的,新手编码人员不会期望这种不必要的行为,但是可以按原样复制/粘贴此行为。也许您可以更新答案?
Orlin Georgiev 2015年

13
我仍然不同意。一些答案应该保持简单,很多来这里的人喜欢复制和粘贴小的代码片段。我只是将原始问题中的Objective-C代码翻译为Swift,除此之外,其他所有内容都是不错的选择。顺便说一句,在这个问题中已经存在一个提供这种奖励信息的答案,这对于重复信息没有多大意义。
卢卡斯·爱德华多

1
@User只需将其替换为image.url任何字符串URL,无论是否经过硬编码:)
Lucas Eduardo

1
@IanWarburton当然,您可以使用任何您想要的:)。1)此答案基于原始问题,该问题在Objective-C中使用了相同的方法,因此我只是帮助快速进行了“翻译”。2)删除此方法并放入并没有任何意义,URLSession.dataTask因为此处的许多其他答案已经显示了如何执行此操作,最好保持打开不同的选项。
卢卡斯·爱德华多

2
如答案中所述,建议的框架Heyo翠鸟Heyo将完成所有繁重的工作。谢谢你的建议!
Kilmazing

68

如果您只想加载图像(异步!),只需将以下扩展名添加到您的快速代码中:

extension UIImageView {
    public func imageFromUrl(urlString: String) {
        if let url = NSURL(string: urlString) {
            let request = NSURLRequest(URL: url)
            NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {
                (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in
                if let imageData = data as NSData? {
                    self.image = UIImage(data: imageData)
                }
            }
        }
    }
}

并以这种方式使用它:

myImageView.imageFromUrl("https://robohash.org/123.png")

1
它没有出现,我认为是因为需要GCD。我该如何刷新?
jo3birdtalk '16

@ jo3birdtalk这不是GCD的问题。使用视图检查绑定。
skywinder

6
NSURLConnection在iOS 9等设备上已被弃用。使用NSURLSession intead。
muhasturk '16

2
在iOS 9中不推荐使用sendAsynchronousRequest
Crashalot '16

感谢skywinder,我正在使用此方法从数组下载图像。我想要当用户按下cancel按钮时停止下载。您是否知道我如何使用此方法来做到这一点?我需要添加取消功能。
ZAFAR007 '16

44

Swift 2.2 || Xcode 7.3

我得到了惊人的结果!与AlamofireImage swift库

它提供了多种功能,例如:

  • 异步下载
  • 如果应用发生内存警告,则自动清除图像缓存
  • 图片网址缓存
  • 图像缓存
  • 避免重复下载

并且非常容易为您的应用实现

步骤1.安装吊舱


Alamofire 3.3.x

豆荚“ Alamofire”

Alamofire图片2.4.x

豆荚“ AlamofireImage”

Step.2导入和使用

import Alamofire
import AlamofireImage

let downloadURL = NSURL(string: "http://cdn.sstatic.net/Sites/stackoverflow/company/Img/photos/big/6.jpg?v=f4b7c5fee820")!
imageView.af_setImageWithURL(downloadURL)

而已!!它将照顾一切


非常感谢Alamofire伙计,他们使iDevelopers的生活变得轻松;)


8
Swift 3:foregroundImage.af_setImage(withURL:将URL作为URL下载)
thejuki 2016年

28

Xcode 8Swift 3

Leo Dabus的回答太棒了!我只想提供一种多功能的解决方案:

let url = URL(string: 
    "http://www.apple.com/euro/ios/ios8/a/generic/images/og.png")

let task = URLSession.shared.dataTask(with: url!) { data, response, error in
    guard let data = data, error == nil else { return }

    DispatchQueue.main.async() {    // execute on main thread
        self.imageView.image = UIImage(data: data)
    }
}

task.resume()

3
马克(Mark),可能在结尾处表示“异步”
Fattie

16

我将对问题的最佳答案的代码包装到扩展UIImageView的单个可重用的类中,因此您可以在故事板上直接使用异步加载UIImageViews(或从代码创建它们)。

这是我的课:

import Foundation
import UIKit

class UIImageViewAsync :UIImageView
{

    override init()
    {
        super.init(frame: CGRect())
    }

    override init(frame:CGRect)
    {
        super.init(frame:frame)
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    func getDataFromUrl(url:String, completion: ((data: NSData?) -> Void)) {
        NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) { (data, response, error) in
            completion(data: NSData(data: data))
        }.resume()
    }

    func downloadImage(url:String){
        getDataFromUrl(url) { data in
            dispatch_async(dispatch_get_main_queue()) {
                self.contentMode = UIViewContentMode.ScaleAspectFill
                self.image = UIImage(data: data!)
            }
        }
    }
}

这是如何使用它:

imageView.downloadImage("http://www.image-server.com/myImage.jpg")

2
我们真的需要那些被覆盖的init吗?最初不是继承吗?似乎我们只是在这里重载,只是要对其自身调用super,这使我感到多余。
纽约市技术工程师

16

斯威夫特4::

这将在加载图像时显示加载器。您可以使用NSCache临时存储图像

let imageCache = NSCache<NSString, UIImage>()
extension UIImageView {
    func loadImageUsingCache(withUrl urlString : String) {
        let url = URL(string: urlString)
        if url == nil {return}
        self.image = nil

        // check cached image
        if let cachedImage = imageCache.object(forKey: urlString as NSString)  {
            self.image = cachedImage
            return
        }

        let activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView.init(activityIndicatorStyle: .gray)
        addSubview(activityIndicator)
        activityIndicator.startAnimating()
        activityIndicator.center = self.center

        // if not, download image from url
        URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
            if error != nil {
                print(error!)
                return
            }

            DispatchQueue.main.async {
                if let image = UIImage(data: data!) {
                    imageCache.setObject(image, forKey: urlString as NSString)
                    self.image = image
                    activityIndicator.removeFromSuperview()
                }
            }

        }).resume()
    }
}

用法:-

truckImageView.loadImageUsingCache(withUrl: currentTruck.logoString)

1
有用。只是在错误情况下也要复制activityIndi​​cator删除。
djdance

1
对我的项目非常有用。
partikles

13
let url = NSURL.URLWithString("http://live-wallpaper.net/iphone/img/app/i/p/iphone-4s-wallpapers-mobile-backgrounds-dark_2466f886de3472ef1fa968033f1da3e1_raw_1087fae1932cec8837695934b7eb1250_raw.jpg");
var err: NSError?
var imageData :NSData = NSData.dataWithContentsOfURL(url,options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &err)
var bgImage = UIImage(data:imageData)

5
fatal error: unexpectedly found nil while unwrapping an Optional value
用户

您有机会编写异步的东西吗?
杰德·格兰特

@JedGrant,您可以使用dispatch_async进入另一个线程和回调
Matej

我必须用“!”打开NSData。(请注意末尾的!),使其像这样工作:var imageData:NSData = NSData(contentsOfURL:url,选项:NSDataReadingOptions.DataReadingMappedIfSafe,错误:&err)!
botbot

13

仅供参考:对于swift-2.0 Xcode7.0 beta2

extension UIImageView {
    public func imageFromUrl(urlString: String) {
        if let url = NSURL(string: urlString) {
            let request = NSURLRequest(URL: url)
            NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {
            (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in
                self.image = UIImage(data: data!)
            }
        }
    }
}

5
NSURLConnection已弃用。您必须使用NSURLSession。最好使用Swift 2.0和Xcode 7进行编码
BilalReffas 2015年

10

迅速3的错误处理

let url = URL(string: arr[indexPath.row] as! String)
if url != nil {
    DispatchQueue.global().async {
        let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
        DispatchQueue.main.async {
            if data != nil {
                cell.imgView.image = UIImage(data:data!)
            }else{
                cell.imgView.image = UIImage(named: "default.png")
            }
        }
    }
}

带扩展

extension UIImageView {

    func setCustomImage(_ imgURLString: String?) {
        guard let imageURLString = imgURLString else {
            self.image = UIImage(named: "default.png")
            return
        }
        DispatchQueue.global().async { [weak self] in
            let data = try? Data(contentsOf: URL(string: imageURLString)!)
            DispatchQueue.main.async {
                self?.image = data != nil ? UIImage(data: data!) : UIImage(named: "default.png")
            }
        }
    }
}

扩展使用

myImageView. setCustomImage("url")

具有缓存支持

let imageCache = NSCache<NSString, UIImage>()

extension UIImageView {

    func loadImageUsingCacheWithURLString(_ URLString: String, placeHolder: UIImage?) {

        self.image = nil
        if let cachedImage = imageCache.object(forKey: NSString(string: URLString)) {
            self.image = cachedImage
            return
        }

        if let url = URL(string: URLString) {
            URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in

                //print("RESPONSE FROM API: \(response)")
                if error != nil {
                    print("ERROR LOADING IMAGES FROM URL: \(String(describing: error))")
                    DispatchQueue.main.async { [weak self] in
                        self?.image = placeHolder
                    }
                    return
                }
                DispatchQueue.main.async { [weak self] in
                    if let data = data {
                        if let downloadedImage = UIImage(data: data) {
                            imageCache.setObject(downloadedImage, forKey: NSString(string: URLString))
                            self?.image = downloadedImage
                        }
                    }
                }
            }).resume()
        }
    }
}

9

Swift 4:使用NSCache的小型图像(例如缩略图)的简单加载器,始终在主线程上运行:

class ImageLoader {

  private static let cache = NSCache<NSString, NSData>()

  class func image(for url: URL, completionHandler: @escaping(_ image: UIImage?) -> ()) {

    DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async {

      if let data = self.cache.object(forKey: url.absoluteString as NSString) {
        DispatchQueue.main.async { completionHandler(UIImage(data: data as Data)) }
        return
      }

      guard let data = NSData(contentsOf: url) else {
        DispatchQueue.main.async { completionHandler(nil) }
        return
      }

      self.cache.setObject(data, forKey: url.absoluteString as NSString)
      DispatchQueue.main.async { completionHandler(UIImage(data: data as Data)) }
    }
  }

}

用法:

ImageLoader.image(for: imageURL) { image in
  self.imageView.image = image
}

为什么将单例与struct一起使用?这将导致您的类不可变,这可能导致您的NSCache问题
XcodeNOOB

感谢您指出这一点,我将其更改为class
fethica

1
私人让缓存给我错误,所以将其更改为静态,它的工作!谢谢
哈马德·塔里克

7

您需要执行以下操作:

UIImage(data: data)

在Swift中,他们已将大多数Objective C工厂方法替换为常规构造函数。

看到:

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-XID_26


2
技术上已映射,未替换。如果您创建自己的方法+(instancetype)[MyThing thingWithOtherThing:],则也将像MyThing(otherThing: ...)在Swift中一样调用它。
Brian Nickel

7

带有错误句柄和自定义请求标头的Swift 2

只需将扩展添加到UIImageView:

extension UIImageView {
    public func imageFromUrl(urlString: String) {
        if let url = NSURL(string: urlString) {
            let request = NSMutableURLRequest(URL: url)
            request.setValue("<YOUR_HEADER_VALUE>", forHTTPHeaderField: "<YOUR_HEADER_KEY>")
            NSURLSession.sharedSession().dataTaskWithRequest(request) {
                (data, response, error) in
                guard let data = data where error == nil else{
                    NSLog("Image download error: \(error)")
                    return
                }

                if let httpResponse = response as? NSHTTPURLResponse{
                    if httpResponse.statusCode > 400 {
                        let errorMsg = NSString(data: data, encoding: NSUTF8StringEncoding)
                        NSLog("Image download error, statusCode: \(httpResponse.statusCode), error: \(errorMsg!)")
                        return
                    }
                }

            dispatch_async(dispatch_get_main_queue(), {
                NSLog("Image download success")
                self.image = UIImage(data: data)
            })
            }.resume()
        }
    }
}

然后,使用新imageFromUrl(urlString: String)的下载图像

用法:

imageView.imageFromUrl("https://i.imgur.com/ONaprQV.png")

在迅速3中,我不断得到这个错误。有什么问题 ?在这一行中,“”“ URLSession.shared.dataTask(with:url){”“”“”“”无法调用类型为'(with:URL,(Data ?, URLResponse ?, Error? )-> Void)'
Aymen BRomdhane

7

斯威夫特4

此方法将异步地从网站下载图像并将其缓存:

    func getImageFromWeb(_ urlString: String, closure: @escaping (UIImage?) -> ()) {
        guard let url = URL(string: urlString) else {
return closure(nil)
        }
        let task = URLSession(configuration: .default).dataTask(with: url) { (data, response, error) in
            guard error == nil else {
                print("error: \(String(describing: error))")
                return closure(nil)
            }
            guard response != nil else {
                print("no response")
                return closure(nil)
            }
            guard data != nil else {
                print("no data")
                return closure(nil)
            }
            DispatchQueue.main.async {
                closure(UIImage(data: data!))
            }
        }; task.resume()
    }

正在使用:

    getImageFromWeb("http://www.apple.com/euro/ios/ios8/a/generic/images/og.png") { (image) in
        if let image = image {
            let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
            imageView.image = image
            self.view.addSubview(imageView)
        } // if you use an Else statement, it will be in background
    }

如何缓存?还有多长时间?
拉米斯

它似乎是永久存储的,这很奇怪,它存储在Library> Caches文件夹中。在模拟器上运行时,请使用print(NSHomeDirectory())到达计算机上的此位置。
Bobby

5

Swift 2.0:

1)

if let url = NSURL(string: "http://etc...") {
    if let data = NSData(contentsOfURL: url) {
        imageURL.image = UIImage(data: data)
    }        
}

要么

imageURL.image =
    NSURL(string: "http:// image name...")
    .flatMap { NSData(contentsOfURL: $0) }
    .flatMap { UIImage(data: $0) }

2)将此方法添加到VC或Extension中。

func load_image(urlString:String)
{   let imgURL: NSURL = NSURL(string: urlString)!
    let request: NSURLRequest = NSURLRequest(URL: imgURL)

    NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) { (response: NSURLResponse?, data: NSData?, error: NSError?) in

        if error == nil {
            self.image_element.image = UIImage(data: data)
        }
    }
}

用法:

self.load_image(" url strig here")

sendAsynchronousRequest在iOS 9中已弃用
Crashalot 2016年

5

Kingfisher是将图像加载到URL的最佳库之一。

Github URL- https://github.com/onevcat/Kingfisher

// If you want to use Activity Indicator.
imageview_pic.kf.indicatorType = .activity
imageview_pic.kf.setImage(with: URL(string: "Give your url string"))

// If you want to use custom placeholder image.
imageview_pic.kf.setImage(with: URL(string: "Give your url string"), placeholder: UIImage(named: "placeholder image name"), options: nil, progressBlock: nil, completionHandler: nil)

5

这是用于从URL加载/下载图像的工作代码。NSCache会自动显示并显示占位符图像,然后再下载并加载实际图像(Swift 4代码)。

func NKPlaceholderImage(image:UIImage?, imageView:UIImageView?,imgUrl:String,compate:@escaping (UIImage?) -> Void){

    if image != nil && imageView != nil {
        imageView!.image = image!
    }

    var urlcatch = imgUrl.replacingOccurrences(of: "/", with: "#")
    let documentpath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
    urlcatch = documentpath + "/" + "\(urlcatch)"

    let image = UIImage(contentsOfFile:urlcatch)
    if image != nil && imageView != nil
    {
        imageView!.image = image!
        compate(image)

    }else{

        if let url = URL(string: imgUrl){

            DispatchQueue.global(qos: .background).async {
                () -> Void in
                let imgdata = NSData(contentsOf: url)
                DispatchQueue.main.async {
                    () -> Void in
                    imgdata?.write(toFile: urlcatch, atomically: true)
                    let image = UIImage(contentsOfFile:urlcatch)
                    compate(image)
                    if image != nil  {
                        if imageView != nil  {
                            imageView!.image = image!
                        }
                    }
                }
            }
        }
    }
}

像这样使用:

// Here imgPicture = your imageView
// UIImage(named: "placeholder") is Display image brfore download and load actual image. 

NKPlaceholderImage(image: UIImage(named: "placeholder"), imageView: imgPicture, imgUrl: "Put Here your server image Url Sting") { (image) in }

1
唯一与我合作的。我的问题是我正在实现音乐播放器,并且我也想在关闭手机时将图像加载到通知中,并且处理程序采用UIImage类型,因此我不得不使用此方法。
JhonnyTawk

4

一种获取安全且可与Swift 2.0和X-Code 7.1一起使用的图像的方法:

static func imageForImageURLString(imageURLString: String, completion: (image: UIImage?, success: Bool) -> Void) {
    guard let url = NSURL(string: imageURLString),
        let data = NSData(contentsOfURL: url),
        let image = UIImage(data: data)
        else { 
            completion(image: nil, success: false); 
            return 
       }

    completion(image: image, success: true)
}

然后,您将这样调用此方法:

imageForImageURLString(imageString) { (image, success) -> Void in
        if success {
            guard let image = image 
                 else { return } // Error handling here 
            // You now have the image. 
         } else {
            // Error handling here.
        }
    }

如果要使用图像更新视图,则必须在“如果成功{”之后使用它:

    dispatch_async(dispatch_get_main_queue()) { () -> Void in
         guard let image = image 
              else { return } // Error handling here 
         // You now have the image. Use the image to update the view or anything UI related here
         // Reload the view, so the image appears
    }

如果要在UI中使用图像,则需要最后一部分的原因是因为网络调用需要时间。如果您尝试使用图像更新UI而不调用像上面那样的dispatch_async,则计算机将在仍在获取图像的同时查找该图像,发现还没有图像,然后像没有图像一样继续前进找到了。将您的代码放入dispatch_async完成闭包中,对计算机说:“去,获得此映像,完成后再完成此代码。” 这样,您将在调用代码时获得图像,并且一切正常。


4

如果您正在寻找一个非常非常简单的实现。(这在Swift 2中对我有用)

 let imageURL = NSURL(string: "https://farm2.staticflickr.com/1591/26078338233_d1466b7da2_m.jpg")
 let imagedData = NSData(contentsOfURL: imageURL!)!
 imageView?.image = UIImage(data: imagedData)

我在一个表格视图中实现了一个只有一个图像的自定义单元格

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{

        let cell = tableView.dequeueReusableCellWithIdentifier("theCell", forIndexPath: indexPath) as! customTableViewCell

        let imageURL = NSURL(string: "https://farm2.staticflickr.com/1591/26078338233_d1466b7da2_m.jpg")

        let imagedData = NSData(contentsOfURL: imageURL!)!

        cell.imageView?.image = UIImage(data: imagedData)

        return cell

    }

没错,那是因为在主线程上进行了下载和压缩。您最好在后台线程中异步执行这两个步骤,可能正在使用dispatch_async全局队列
Naishta,2013年

是的,通过使用后台线程,我们可以优化下载速度,并且如果需要,我们可以更改逻辑而不是逻辑,我们可以使用sdWebImage或其他框架。
Gourav Joshi

2

我建议使用Kingfisher库异步下载图像。关于使用Kingfisher最好的部分是,默认情况下,它将图像URL作为ID缓存所有下载的图像。下次当您请求下载带有该特定URl的图像时,它将从缓存中加载它。

用法:

newsImage.kf.setImage(with: imageUrl!, placeholder: nil, options: nil, progressBlock: nil, completionHandler: { (image, error, cacheType, imageUrl) in
                if error == nil{
                    self.activityIndicator.stopAnimating()
                }else if error != nil{
                    self.activityIndicator.stopAnimating()
                }
            })

2

您可以使用pod SDWebImage实现相同的目的。它易于使用。哟可以在这里得到文件建立SDWebImage

这是示例代码

self.yourImage.sd_setImage(with: NSURL(string: StrUrl as String ) as URL!, placeholderImage: placeholderImage, options: SDWebImageOptions(rawValue: 0), completed: { (image, error, cacheType, imageURL) in
                if( error != nil)
                {
                    print("Error while displaying image" , (error?.localizedDescription)! as String)
                }
            })

1

唯一缺少的是!

let url = NSURL.URLWithString("http://live-wallpaper.net/iphone/img/app/i/p/iphone-4s-wallpapers-mobile-backgrounds-dark_2466f886de3472ef1fa968033f1da3e1_raw_1087fae1932cec8837695934b7eb1250_raw.jpg");
var err: NSError?
var imageData :NSData = NSData.dataWithContentsOfURL(url!,options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &err)
var bgImage = UIImage(data:imageData!)

抱歉,您的回复很晚,您能更精确地知道遇到什么错误吗?
西蒙·詹森

1

Swift 2.x答案将图像下载到文件(与Leo Dabus的答案相反,后者将图像存储在内存中)。基于Leo Dabus的回答和Rob的回答,从完成处理程序的NSURLSession DownloadTaskWithRequest获取数据

    // Set download vars
    let downloadURL = NSURL() // URL to download from
    let localFilename = "foobar.png" // Filename for storing locally 

    // Create download request
    let task = NSURLSession.sharedSession().downloadTaskWithURL(downloadURL) { location, response, error in
        guard location != nil && error == nil else {
            print("Error downloading message: \(error)")
            return
        }

        // If here, no errors so save message to permanent location
        let fileManager = NSFileManager.defaultManager()
        do {
            let documents = try fileManager.URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
            let fileURL = documents.URLByAppendingPathComponent(localFilename)
            try fileManager.moveItemAtURL(location!, toURL: fileURL)
            self.doFileDownloaded(fileURL, localFilename: localFilename)
            print("Downloaded message @ \(localFilename)")
        } catch {
            print("Error downloading message: \(error)")
        }
    }

    // Start download
    print("Starting download @ \(downloadURL)")
    task.resume()


// Helper function called after file successfully downloaded
private func doFileDownloaded(fileURL: NSURL, localFilename: String) {

    // Do stuff with downloaded image

}

1

Swift 4.1我创建了一个仅传递图像URL的函数,在图像生成后将其设置为完成块。

   class NetworkManager: NSObject {

  private var imageQueue = OperationQueue()
  private var imageCache = NSCache<AnyObject, AnyObject>()

  func downloadImageWithUrl(imageUrl: String, cacheKey: String, completionBlock: @escaping (_ image: UIImage?)-> Void) {

    let downloadedImage = imageCache.object(forKey: cacheKey as AnyObject)
    if let  _ = downloadedImage as? UIImage {
      completionBlock(downloadedImage as? UIImage)
    } else {
      let blockOperation = BlockOperation()
      blockOperation.addExecutionBlock({
        let url = URL(string: imageUrl)
        do {
          let data = try Data(contentsOf: url!)
          let newImage = UIImage(data: data)
          if newImage != nil {
            self.imageCache.setObject(newImage!, forKey: cacheKey as AnyObject)
            self.runOnMainThread {
              completionBlock(newImage)
            }
          } else {
            completionBlock(nil)
          }
        } catch {
          completionBlock(nil)
        }
      })
      self.imageQueue.addOperation(blockOperation)
      blockOperation.completionBlock = {
        print("Image downloaded \(cacheKey)")
      }
    }
  }
}
extension NetworkManager {
  fileprivate func runOnMainThread(block:@escaping ()->Void) {
    if Thread.isMainThread {
      block()
    } else {
      let mainQueue = OperationQueue.main
      mainQueue.addOperation({
        block()
      })
    }
  }
}

1
class func downloadImageFromUrl(with urlStr: String, andCompletionHandler:@escaping (_ result:Bool) -> Void) {
        guard let url = URL(string: urlStr) else {
            andCompletionHandler(false)
            return
        }
        DispatchQueue.global(qos: .background).async {
            URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) -> Void in
                if error == nil {
                    let httpURLResponse = response as? HTTPURLResponse
                    Utils.print( "status code ID : \(String(describing: httpURLResponse?.statusCode))")
                    if httpURLResponse?.statusCode == 200 {
                        if let data = data {
                            if let image = UIImage(data: data) {
                                ImageCaching.sharedInterface().setImage(image, withID: url.absoluteString as NSString)
                                DispatchQueue.main.async {
                                    andCompletionHandler(true)
                                }
                            }else {
                                andCompletionHandler(false)
                            }
                        }else {
                            andCompletionHandler(false)
                        }
                    }else {
                        andCompletionHandler(false)
                    }
                }else {
                    andCompletionHandler(false)
                }
            }).resume()
        }
    }

我在类中创建了一个简单的类函数,Utils.swift用于调用该方法,您可以通过该方法简单地访问该方法,classname.methodname并使用ImageCaching.swift类将图像保存在NSCache中

Utils.downloadImageFromUrl(with: URL, andCompletionHandler: { (isDownloaded) in
                            if isDownloaded {
                                if  let image = ImageCaching.sharedInterface().getImage(URL as NSString) {
                                    self.btnTeam.setBackgroundImage(image, for: .normal)
                                }
                            }else {
                                DispatchQueue.main.async {
                                    self.btnTeam.setBackgroundImage(#imageLiteral(resourceName: "com"), for: .normal)
                                }
                            }
                        })

快乐的外衣。干杯:)


1

Swift 4.2和AlamofireImage

如果使用库不是问题,可以在的帮助下进行AlamofireImage。我的样本来自Github

占位符图像示例:

let imageView = UIImageView(frame: frame)
let url = URL(string: "https://httpbin.org/image/png")!
let placeholderImage = UIImage(named: "placeholder")!
imageView.af_setImage(withURL: url, placeholderImage: placeholderImage)

它具有许多方便的功能和扩展功能,可以处理图像。从缓存到缩放和调整大小,甚至在图像上应用滤镜。如果图片对您的应用很重要,我建议您使用此框架并节省时间。


1

迅捷5

extension UIImageView {
    func load(url: URL) {
        DispatchQueue.global().async { [weak self] in
            if let data = try? Data(contentsOf: url) {
                if let image = UIImage(data: data) {
                    DispatchQueue.main.async {
                        self?.image = image
                    }
                }
            }
        }
    }
}

用于

override func awakeFromNib() {
    super.awakeFromNib()
    imgView.load(url: "<imageURLHere>")
}

0

使用Ascyimageview,您可以轻松在imageview中加载imageurl。

让image1Url:URL = URL(string:“(imageurl)”作为String)!imageview.imageURL = image1Url


0

从服务器加载图像:-

func downloadImage(from url: URL , success:@escaping((_ image:UIImage)->()),failure:@escaping ((_ msg:String)->())){
    print("Download Started")
    getData(from: url) { data, response, error in
        guard let data = data, error == nil else {
            failure("Image cant download from G+ or fb server")
            return
        }

        print(response?.suggestedFilename ?? url.lastPathComponent)
        print("Download Finished")
        DispatchQueue.main.async() {
             if let _img = UIImage(data: data){
                  success(_img)
            }
        }
    }
}
func getData(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ()) {
    URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}

用法:-

  if let url = URL(string: "http://www.apple.com/euro/ios/ios8/a/generic/images/og.png") {
                        self.downloadImage(from:url , success: { (image) in
                            print(image)

                        }, failure: { (failureReason) in
                            print(failureReason)
                        })
                    }

0

为了获得更好的性能UITableViewUICollectionView使用轻量级库,请执行以下操作:智能延迟加载如果要从url异步加载图像,可以使用此延迟加载方法。

智能“延迟加载”中UICollectionViewUITableView使用NSOperationNSOperationQueueiOS中所以在这个项目中,我们可以下载多张图片中的任何视图(UICollectionViewUITableView通过使用优化的应用程序的性能)OperationOperationQueue并发性。以下是该项目的关键点:智能延迟加载:创建图像下载服务。根据单元的可见性优先下载。

ImageDownloadService类将创建一个单例实例,并具有NSCache实例来缓存已下载的图像。我们已经将Operation类继承到TOperation以根据需要调整功能。我认为操作子类的属性在功能方面非常清楚。我们正在使用KVO监视操作状态的变化。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.