【问题标题】:Loading/Downloading image from URL on Swift在 Swift 上从 URL 加载/下载图像
【发布时间】:2014-08-05 13:39:36
【问题描述】:

我想从我的应用程序中的 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,我该如何解决这个问题。

【问题讨论】:

  • 试试这个imageURL.image = UIImage(data: myDataVar)
  • 完美!谢谢 但是我不明白为什么这个方法在 Objective C 中起作用,而不是在 Swift 中……奇怪
  • 当你在使用 Cocoa 类时遇到问题,尝试 CMD+单击类名,你应该可以看到该类的 Swift 界面!
  • if let url = NSURL(string: "imageurl") { if let data = NSData(contentsOfURL: url) { imageView.image = UIImage(data: data) } }
  • @LeoDabus 没关系。据我了解,“swift”标签将始终引用当前版本。指定版本的标签用于与版本特定语言功能相关的问题。这不是确定的,但请查看this meta post

标签: ios swift uiimage nsurl


【解决方案1】:

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
}

异步:

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

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")
        // always update the UI from the main thread
        DispatchQueue.main.async() { [weak self] in
            self?.imageView.image = UIImage(data: data)
        }
    }
}

用法:

override func viewDidLoad() {
    super.viewDidLoad()
    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: ContentMode = .scaleAspectFit) {
        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: ContentMode = .scaleAspectFit) { 
        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")

【讨论】:

  • 这里只是一个旁注,你应该给它设置一个关联的对象;否则,您可能会在彼此之上加载图像。例如。在 UITableView 中,其中一个单元格显示图像,并且在返回出列单元格时更新图像。如果进程#1 比进程#2 花费更长的时间,进程#2 将显示其图像,然后进程#1 将更新它,即使该图像对用户不再有效。
  • @PaulPeelen 您能否添加有关如何完成此操作的更多信息?如果我们能让这个很棒的答案变得更好,那就太好了。
  • @AndHeiberg 检查objc_setAssociatedObjectobjc_removeAssociatedObjects
  • @LeoDabus 你怎么用 dataTaskWithURL 而不是 downloadTaskWithURL?
  • 第一个下载到内存第二个到文件
【解决方案2】:

(Swift 4 更新) 为了直接回答原始问题,这里是发布的 Objective-C sn-p 的 swift 等价物。

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!)
    }
}

也就是说,在现实生活中的应用程序中,如果您希望获得最佳用户体验并避免多次下载同一张图像,您可能还希望不仅下载它们,而且还希望它们被缓存。已经有相当多的库可以非常无缝地做到这一点,而且它们都非常易于使用。我个人推荐Kingfisher

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) 

就是这样

【讨论】:

  • "Easily" 是主观的,新手编码人员不会想到这种不受欢迎的行为,但会照原样复制/粘贴它。也许你可以更新你的答案?
  • 我还是不同意。有些答案应该保持简单,很多来这里的人都喜欢复制和粘贴小的sn-ps代码。我刚刚将原始问题中的 Objective-C 代码翻译成 Swift,除此之外的一切都是额外的。顺便说一句,提供这种奖励信息的同一个问题已经有一个答案,这对重复信息的意义不大。
  • @User 只需将 image.url 替换为任何字符串 url,无论是否硬编码 :)
  • @IanWarburton 当然你可以使用任何你想要的东西:)。 1)这个答案基于原始问题,它在objective-C中使用相同的方法,所以我只是帮助“翻译”到swift。 2)删除这种方法并放置URLSession.dataTask是没有意义的,因为这里的许多其他答案已经显示了如何做到这一点,最好保持不同的选项打开。
  • 嘿嘿,建议的框架,翠鸟,如答案中所述,将完成所有繁重的工作。感谢您的建议!
【解决方案3】:

如果您只想加载图像(异步!) - 只需将这个小扩展添加到您的 swift 代码中:

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")

【讨论】:

  • 它没有出现,我认为是因为需要 GCD。如何刷新?
  • @jo3birdtalk 这不是 GCD 的问题。用视图检查你的绑定。
  • NSURLConnection 在 iOS 9 等上已弃用。而是使用 NSURLSession。
  • sendAsynchronousRequest 在 iOS 9 中已弃用
  • 感谢skywinder,我正在使用这种方法从数组中下载图像。我希望当用户按下cancel 按钮时它会停止下载。你知道我怎么能用这种方法做到这一点吗?我需要添加取消功能。
【解决方案4】:

Swift 2.2 || Xcode 7.3

我得到了惊人的结果!!使用 AlamofireImage 快速库

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

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

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

Step.1 安装 pod


Alamofire 3.3.x

pod 'Alamofire'

AlamofireImage 2.4.x

pod '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 guys,让 iDevelopers 的生活变得轻松;)

【讨论】:

  • Swift 3:foregroundImage.af_setImage(withURL: downloadURL as URL)
  • 谢谢,伙计,这对我有帮助。
【解决方案5】:

Xcode 12Swift 5

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

if 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()
}

【讨论】:

    【解决方案6】:

    斯威夫特 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)
    

    【讨论】:

    • 它有效。在错误情况下也只需复制 activityIndi​​cator 删除即可。
    【解决方案7】:

    我将问题最佳答案的代码封装到一个扩展 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")
    

    【讨论】:

    • 我们真的需要那些被覆盖的init吗?无论如何,init都不是继承的吗?似乎我们只是在这里覆盖只是为了调用 super 本身,这让我觉得是多余的。
    【解决方案8】:

    带有错误处理的swift 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】:
      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)
      

      【讨论】:

      • fatal error: unexpectedly found nil while unwrapping an Optional value
      • 你有机会写一些异步的东西吗?
      • @JedGrant 你可以使用 dispatch_async 进入另一个线程和回调
      • 我必须用“!”解开 NSData (注意最后的 ! )让它像这样工作: var imageData :NSData = NSData(contentsOfURL: url, options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &err)!
      【解决方案10】:

      仅供参考:对于 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!)
                  }
              }
          }
      }
      

      【讨论】:

      • NSURLConnection 已弃用。你必须使用 NSURLSession。使用 Swift 2.0 和 Xcode 7 编码会更好
      【解决方案11】:

      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
      }
      

      【讨论】:

      • private let 缓存给了我错误,所以将其更改为静态并且它有效!谢谢
      • 我认为你不应该在主线程上加载UIImage(data: data as Data)。这应该在后台线程中完成。
      【解决方案12】:

      你会想要做的:

      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

      【讨论】:

      • 技术映射,未替换。如果您创建自己的方法+(instancetype)[MyThing thingWithOtherThing:],您也可以在 Swift 中将其称为MyThing(otherThing: ...)
      【解决方案13】:

      带有错误句柄和自定义请求标头的 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")
      

      【讨论】:

      • 在 swift 3 中,我不断收到此错误。有什么问题 ?在这一行中“”“URLSession.shared.dataTask(with: url){”“”“”“”不能调用'dataTask'类型的参数列表'(with: URL, (Data?, URLResponse?, Error? ) -> 无效)'
      【解决方案14】:

      斯威夫特 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()) 到达您计算机上的此位置。
      【解决方案15】:

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

      Github 网址 - 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)
      

      【讨论】:

        【解决方案16】:

        斯威夫特 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()
           if let url = URL(string:"<imageURLHere>"){
             imgView.load(url: url)
           }
        }
        

        【讨论】:

          【解决方案17】:

          这是从 URL 加载/下载图像的工作代码。 NSCache 自动并在下载和加载实际图像之前显示占位符图像(Swift 4 | Swift 5 代码)。

          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 }
          

          【讨论】:

          • 唯一对我有用的。我的问题是我正在实现音乐播放器,我想在手机关闭时将图像加载到通知中,并且处理程序采用 UIImage 的类型,所以我不得不使用这个方法。
          • 您如何处理下载文件夹的偶尔清理?与 NSCache 相比,图像的重新加载更快、更流畅。但它不希望将图像下载到磁盘。
          【解决方案18】:

          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 中已弃用
          【解决方案19】:
          class ImageStore: NSObject { 
              static let imageCache = NSCache<NSString, UIImage>()
          }
          
          extension UIImageView {
              func url(_ url: String?) {
                  DispatchQueue.global().async { [weak self] in
                      guard let stringURL = url, let url = URL(string: stringURL) else {
                          return
                      }
                      func setImage(image:UIImage?) {
                          DispatchQueue.main.async {
                              self?.image = image
                          }
                      }
                      let urlToString = url.absoluteString as NSString
                      if let cachedImage = ImageStore.imageCache.object(forKey: urlToString) {
                          setImage(image: cachedImage)
                      } else if let data = try? Data(contentsOf: url), let image = UIImage(data: data) {
                          DispatchQueue.main.async {
                              ImageStore.imageCache.setObject(image, forKey: urlToString)
                              setImage(image: image)
                          }
                      }else {
                          setImage(image: nil)
                      }
                  }
              }
          }
          

          用法:

          let imageView = UIImageView()
          imageView.url("image url")
          

          【讨论】:

            【解决方案20】:

            如果您想快速检查来自 url 的图像,快速破解

             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 全局队列
            • 是的,通过使用后台线程我们可以优化下载速度,如果需要我们可以改变逻辑而不是这个,我们可以使用 sdWebImage 或其他框架。
            【解决方案21】:

            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()
                  })
                }
              }
            }
            

            【讨论】:

              【解决方案22】:

              一种获取安全且适用于 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.
                      }
                  }
              

              如果您使用图像更新视图,则必须在“if success {”之后使用它:

                  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 完成闭包中,对计算机说:“去,获取此图像,完成后,完成此代码。”这样,当代码被调用时,您将拥有图像并且一切都会正常运行。

              【讨论】:

                【解决方案23】:

                我建议使用 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()
                                }
                            })
                

                【讨论】:

                  【解决方案24】:

                  您可以使用 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)
                                  }
                              })
                  

                  【讨论】:

                    【解决方案25】:

                    从服务器加载图像:-

                    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)
                                            })
                                        }
                    

                    【讨论】:

                      【解决方案26】:

                      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)
                      

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

                      【讨论】:

                        【解决方案27】:

                        AsyncImage 在iOS 15之后正式引入,一个同步加载和显示图片的视图。

                         var imageView : AsyncImage
                        
                        imageView = AsyncImage(url: URL(string: entry.photo))
                            .frame(width: 200, height: 200)
                        

                        它还支持:

                        doc中查看更多信息

                        【讨论】:

                          【解决方案28】:

                          唯一缺少的是一个!

                          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!)
                          

                          【讨论】:

                          • 抱歉回复晚了,您能更准确地说明您遇到的错误吗?
                          【解决方案29】:

                          将图像下载到文件的 Swift 2.x 答案(与 Leo Dabus 的答案相反,它将图像存储在内存中)。基于 Leo Dabus 的回答和 Rob 来自Get the data from NSURLSession DownloadTaskWithRequest from completion handler 的回答:

                              // 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
                          
                          }
                          

                          【讨论】:

                            【解决方案30】:
                            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)
                                                            }
                                                        }
                                                    })
                            

                            快乐编码。干杯:)

                            已针对 09/2021 的最新更改进行了编辑

                            // It's better to use extension 
                            extension UIImageView {
                            func downloadImage(from URLString: String, with completion: @escaping (_ response: (status: Bool, image: UIImage? ) ) -> Void) {
                                guard let url = URL(string: URLString) else {
                                    completion((status: false, image: nil))
                                    return
                                }
                                
                                URLSession.shared.dataTask(with: url) { data, response, error in
                                    guard error == nil else {
                                        completion((status: false, image: nil))
                                        return
                                    }
                                    
                                    guard let httpURLResponse = response as? HTTPURLResponse,
                                          httpURLResponse.statusCode == 200,
                                          let data = data else {
                                        completion((status: false, image: nil))
                                        return
                                    }
                                    
                                    let image = UIImage(data: data)
                                    completion((status: true, image: image))
                                }.resume()
                            }
                            }
                            

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 2017-07-19
                              • 1970-01-01
                              • 1970-01-01
                              • 2016-07-17
                              • 2023-03-13
                              相关资源
                              最近更新 更多