【问题标题】:Display image from URL, Swift 4.2从 URL 显示图像,Swift 4.2
【发布时间】:2019-04-14 13:44:15
【问题描述】:

我是一个相当不错的 Objective C 开发人员,我现在正在学习 Swift(我觉得这很困难,这不仅是因为新概念,例如可选项,还因为 Swift 不断发展,而且很多可用的教程严重过时)。

目前我正在尝试将 JSON 从 url 解析为 NSDictionary,然后使用它的一个值来显示图像(这也是一个 url)。像这样的:


URL -> NSDictionary -> 从 url 初始化 UIImage -> 在 UIImageView 中显示 UIImage


这在 Objective C 中很容易(甚至可能有更简短的答案):

NSURL *url = [NSURL URLWithString:@"https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY"];
NSData *apodData = [NSData dataWithContentsOfURL:url];
NSDictionary *apodDict = [NSJSONSerialization JSONObjectWithData:apodData options:0 error:nil];

上面的代码sn-p给了我一个标准的NSDictionary,我可以在其中引用“url”键来获取我要显示的图片的地址:


“网址”:“https://apod.nasa.gov/apod/image/1811/hillpan_apollo15_4000.jpg


然后我将其转换为 UIImage 并将其提供给 UIImageView:

NSURL *imageURL = [NSURL URLWithString: [apodDict objectForKey:@"url"]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *apodImage = [UIImage imageWithData:imageData];

UIImageView *apodView = [[UIImageView alloc] initWithImage: apodImage];

现在,我基本上是在尝试在 Swift 中复制上述 Objective C 代码,但不断遇到困难。我已经尝试了几个教程(其中一个实际上做了完全相同的事情:显示 NASA 图像),并找到了一些堆栈溢出答案,但没有一个可以提供帮助,因为它们要么已经过时,要么做的事情与我需要的不同。

所以,我想请社区为这些问题提供 Swift 4 代码:

1. Convert data from url into a Dictionary
2. Use key:value pair from dict to get url to display an image

如果还不算太多,我还想在代码旁边要求详细描述,因为我希望答案是我认为目前在任何地方都没有的针对此任务的一个全面的“教程”。

谢谢!

【问题讨论】:

  • 在 Swift 中你应该使用URL,而不是NSURL。使用Data,而不是NSData。使用 Swift 字典,而不是 NSDictionary

标签: ios json swift nsdictionary


【解决方案1】:

首先我很确定在半年内你会发现Objective-C 非常复杂和困难。 ?

其次,甚至不鼓励您使用 ObjC 代码。不要使用同步 Data(contentsOf 方法从远程 URL 加载数据。无论使用哪种语言,都可以使用异步方式,例如 (NS)URLSession

并且不要在 Swift 中使用 Foundation 集合类型 NSArrayNSDictionary。如果有原生的 Swift 对应物,基本上不要使用 NS... 类。

在 Swift 4 中,您可以使用 Decodable 协议轻松地将 JSON 直接解码为 (Swift) 结构,
URL 字符串甚至可以解码为URL

创建一个结构

struct Item: Decodable {
    // let copyright, date, explanation: String
    // let hdurl: String
    // let mediaType, serviceVersion, title: String
    let url: URL
}

如果您需要的不仅仅是 URL,请取消注释这些行。

并使用两个数据任务加载数据。

let url = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")! 

let task = URLSession.shared.dataTask(with: url) { (data, _, error) in
    if let error = error { print(error); return }
    do {
       let decoder = JSONDecoder()
       // this line is only needed if all JSON keys are decoded
       decoder.keyDecodingStrategy = .convertFromSnakeCase
       let result = try decoder.decode(Item.self, from: data!)
       let imageTask = URLSession.shared.dataTask(with: result.url) { (imageData, _, imageError) in
           if let imageError = imageError { print(imageError); return }
           DispatchQueue.main.async {
               let apodImage = UIImage(data: imageData!)
               let apodView = UIImageView(image: apodImage)
               // do something with the image view
           }
       }
       imageTask.resume()
   } catch { print(error) }
}
task.resume()

【讨论】:

  • Vadian,你好像知道自己在做什么,谢谢你的回答!但是,在我接受之前,我想请您进行一些修改: 1. 您是否介意修改您的 main 函数以仅返回一个 NSDictionary 对象(因为代码简洁,所以每个函数只执行一项任务)。 2. 你介意评论你的代码,这样我就知道每一行是做什么的以及为什么/如何?这对于理解您的答案很重要,而不仅仅是复制/粘贴。我知道这很多,但我认为需要填补当前缺少此任务的教程的空白。
  • 正如我所说,不要在 Swift 中使用 NSDictionary。结构体swiftier,更安全、更高效。异步任务不 return 任何内容,因此您需要一个(或两个)完成处理程序。这里有无数关于URLSession 和完成处理程序的问题。
  • 对不起,不是 NSDictionary,结构。你说的对!但更重要的是我还是看不懂你的代码。 :(此时,我所能做的就是复制和粘贴它,这不是我要找的;我想了解我在做什么。我也认为遵守干净的编码规则并让函数很重要处理特定任务,例如将 JSON 解码为与语言兼容的数据对象。这也有助于我更好地理解您的解决方案,因为它会分解为更小的任务。
【解决方案2】:

你需要将url转换成字符串和数据才能添加到imageview中

let imageURL:URL=URL(string: YourImageURL)!
let data=NSData(contentsOf: imageURL)
Yourimage.image=UIImage(data: data! as Data)

【讨论】:

    【解决方案3】:

    由于图像加载是一项微不足道且同时可以通过多种不同方式实现的任务,因此我建议您不要“重新发明轮子”并查看诸如 @ 之类的图像加载库987654321@,因为它已经涵盖了您在开发过程中可能需要的大部分案例。

    它允许您使用简单的 api 将图像异步加载和显示到您的视图中:

    Nuke.loadImage(with: url, into: imageView)
    

    如果您需要 - 指定 如何 加载和呈现图像:

    let options = ImageLoadingOptions(
    placeholder: UIImage(named: "placeholder"),
    failureImage: UIImage(named: "failure_image"),
    contentModes: .init(
        success: .scaleAspectFill,
        failure: .center,
        placeholder: .center
    )
    )
    Nuke.loadImage(with: url, options: options, into: imageView)
    

    【讨论】:

      【解决方案4】:

      首先在 Podfile 中添加 pod 豆荚'Alamofire', 吊舱'AlamofireImage' 您可以查看此链接以安装 pod => https://cocoapods.org/pods/AlamofireImage

      // 在imageview中使用该函数从URL加载图片

      imageView.af_setImage(
          withURL: url,
          placeholderImage: placeholderImage //its optional if you want to add placeholder
      )
      

      查看此链接了解 alamofireImage 的方法 https://github.com/Alamofire/AlamofireImage/blob/master/Documentation/AlamofireImage%203.0%20Migration%20Guide.md

      【讨论】:

        【解决方案5】:

        创建一个UIIimageView Extension和如下代码

        extension UIImageView {
        public func imageFromServerURL(urlString: String) {
            self.image = nil
            let urlStringNew = urlString.replacingOccurrences(of: " ", with: "%20")
            URLSession.shared.dataTask(with: NSURL(string: urlStringNew)! as URL, completionHandler: { (data, response, error) -> Void in
        
                if error != nil {
                    print(error as Any)
                    return
                }
                DispatchQueue.main.async(execute: { () -> Void in
                    let image = UIImage(data: data!)
                    self.image = image
                })
        
            }).resume()
        }}
        

        self.UploadedImageView.imageFromServerURL(urlString: imageURLStirng!)
        

        【讨论】:

          【解决方案6】:

          我刚刚扩展了 vadian 的答案,分离了一些关注点以清楚地了解基础知识。他的回答应该足够了。

          首先,您必须构建您的结构。这将代表您从网络服务中检索到的 JSON 结构。

          struct Item: Codable {
              let url, hdurl : URL,
              let copyright, explanation, media_type, service_version, title : String
          }
          

          然后让你请求方法。我通常为它创建一个单独的文件。现在,vadian 提到了完成处理程序。这些由转义闭包表示。在这里,closure ()-> 被传递给两个函数,并以解码后的数据作为参数调用。

          struct RequestCtrl {
          
              func fetchItem(completion: @escaping (Item?)->Void) {
          
                   let url = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")!
                   //URLSessionDataTask handles the req and returns the data which you will decode based on the Item structure we defined above.
                   let task = URLSession.shared.dataTask(with: url) { (data, _, _) in 
                       let jsonDecoder = JSONDecoder()
                       if let data = data,
                          let item = try? jsonDecoder.decode(Item.self, from: data){
                          //jsonDecoder requires a type of our structure represented by .self and the data from the request.  
                          completion(item)
                       } else {
                           completion(nil)
                       }
                    }
                   task.resume()
              }
          
          
              func fetchItemPhoto(usingURL url: URL, completion: @escaping (Data?)-> Void) {
                   let task = URLSession.shared.dataTask(with: url) { (data, _, _) in
                      if let data = data { completion(data) } else { completion(nil) }
                    }
                   task.resume()
              }
          }
          

          现在在你的 ViewController 中,调用你的请求并处理你的闭包的执行。

            class ViewController: UIViewController {
          
                let requestCtrl = RequestCtrl()
          
                override func viewDidLoad() {
                   super.viewDidLoad()
          
                   requestCtrl.fetchItem { (fetchedItem) in
                      guard let fetchedItem = fetchedItem else { return }
                      self.getPhoto(with: fetchedItem)
                   }
          
                }
          
                func getPhoto(with item: Item) {
                     requestCtrl.fetchItemPhoto(usingURL: item.url) { (fetchedPhoto) in
                           guard let fetchedPhoto = fetchedPhoto else { return }
                           let photo = UIImage(data: fetchedPhoto)
                            //now you have a photo at your disposal 
                     }
                }
            }
          

          这些不是最佳实践,因为我还在学习,所以一定要对 Apple 文档中的闭包、ios 并发和 URLComponents 等主题进行一些研究:)

          【讨论】:

            【解决方案7】:

            您可以使用此扩展程序

            extension UIImage {
            
                public static func loadFrom(url: URL, completion: @escaping (_ image: UIImage?) -> ()) {
                    DispatchQueue.global().async {
                        if let data = try? Data(contentsOf: url) {
                            DispatchQueue.main.async {
                                completion(UIImage(data: data))
                            }
                        } else {
                            DispatchQueue.main.async {
                                completion(nil)
                            }
                        }
                    }
                }
            
            }
            

            使用

            guard let url = URL(string: "http://myImage.com/image.png") else { return }
            
            UIImage.loadFrom(url: url) { image in
                self.photo.image = image
            }
            

            【讨论】:

              猜你喜欢
              • 2022-07-05
              • 2017-02-10
              • 2021-09-07
              • 1970-01-01
              • 1970-01-01
              • 2014-11-29
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多