Swift - 在单独的文件

时间:2016-10-25 23:55:00

标签: ios swift multithreading

我正在尝试在Swift中使用多线程来检索和显示视图控制器中的图像。但是,我用来检索图像的功能是在一个单独的模型中。我可以将该函数复制并粘贴到视图控制器中,但我将多次重复使用此函数,并希望将其与特定的视图控制器分开。

我目前拥有的功能(再次,在一个名为WikimediaAPI的单独文件中)如下:

public func getThumbnailImage(forPage page: String) -> UIImage? {
        if let data = try? Data(contentsOf: URL(string: "https://en.wikipedia.org/w/api.php?action=query&titles=" + page + "&prop=pageimages&format=json&pithumbsize=1000")!) {
            let json = JSON(data: data)
            if let pageId = json["query"]["pages"].dictionary?.first?.key {
                if let imageUrl = json["query"]["pages"][pageId]["thumbnail"]["source"].string {
                    if let url = URL(string: imageUrl) {
                        if let imageData = try? Data(contentsOf: url) {
                            if let image = UIImage(data: imageData) {
                                return image
                            }
                        }
                    }
                }
            }
        }
        return nil
    }

上面的工作,但不是多线程的,当我从表视图控制器切换到视图控制器时,它需要太长时间。我试图在这些行的某处实现多线程,但我不允许在异步块中返回任何内容

public func getThumbnailImage(forPage page: String) -> UIImage? {
        DispatchQueue.global().async {
            if let data = try? Data(contentsOf: URL(string: "https://en.wikipedia.org/w/api.php?action=query&titles=" + page + "&prop=pageimages&format=json&pithumbsize=1000")!) {
                DispatchQueue.main.async {
                    let json = JSON(data: data)
                    if let pageId = json["query"]["pages"].dictionary?.first?.key {
                        if let imageUrl = json["query"]["pages"][pageId]["thumbnail"]["source"].string {
                            if let url = URL(string: imageUrl) {
                                DispatchQueue.global().async {
                                    if let imageData = try? Data(contentsOf: url) {
                                        DispatchQueue.main.async {
                                            if let image = UIImage(data: imageData) {
                                                return image //error: "Unexpected non-void return value in void function"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return nil
    }

我认为有可能使用异步代码块而不是UIImage返回一个闭包,但我不确定如何实现它。而且,如果我这样做,我仍然需要在视图控制器中设置图像,我不知道该怎么做。

以下是对视图控制器中函数的调用:

let wikiQuery = WikimediaAPI() // the class from above

fileprivate func setImage() {
        if let pageTitle = wikimediaPageTitleDict[(allergy.0).lowercased()] {
            if let image = wikiQuery.getThumbnailImage(forPage: pageTitle) {
                wikipediaImage.image = image
                wikipediaImage.contentMode = .scaleAspectFit
            }

        }

  }

我正在使用plist中的字典(wikimediaPageTitleDict)将字符串与正确的维基百科页面关联起来(allergy.0就是那个字符串)

所有帮助表示赞赏。谢谢!

3 个答案:

答案 0 :(得分:1)

AlamofireImageSDWebImage都会为您执行此操作,并且它们会扩展UIImageView,因此您只需在图像视图上调用方法,并在下载(或从缓存中检索)图像时获取图像视图更新。我建议你使用一个库。但是,如果你想自己做(注意守卫消除如果让金字塔的厄运):

    typealias ImageCallback = (UIImage?) -> Void

    func getThumbnailImage(forPage page: String, completion: @escaping ImageCallback) -> void {
        DispatchQueue.global().async {
            var imageToReturn: UIImage? = nil
            defer {
                completion(imageToReturn)
            }
            guard let data = try? Data(contentsOf: URL(string: "https://en.wikipedia.org/w/api.php?action=query&titles=" + page + "&prop=pageimages&format=json&pithumbsize=1000")!) else {
                return
            }
            let json = JSON(data: data)
            guard let pageId = json["query"]["pages"].dictionary?.first?.key,
                  let imageUrl = json["query"]["pages"][pageId]["thumbnail"]["source"].string,
                  let url = URL(string: imageUrl) else {
                return
            }
            guard let imageData = try? Data(contentsOf: url),
                  let image = UIImage(data: imageData) else {
                return
            }
            imageToReturn = image
        }

    }

    getThumbnailImage(forPage: "Escherichia_coli") { image in
        DispatchQueue.main.async {
            imageView.image = image
        }
    }

答案 1 :(得分:0)

如果您使用的是异步代码,则无法从函数返回结果。这不是异步的工作方式。异步函数调用将结果排队,以便稍后获取。然后函数在结果可用之前返回。

您通常使用异步函数执行的操作是传入一个在结果可用时调用的完成块。您可以传入一个闭包,将新下载的图像安装到视图控制器中的相应图像视图中,例如

在这个帖子上查看我的回答。

Storing values in completionHandlers - Swift

我给出了一个带有完成处理程序的异步函数的工作示例。

答案 2 :(得分:0)

您无法在调度异步关闭中返回任何内容。 但是,您可以将UIImageView变量传递给您 getThumbnailImage当您收到图片时, 你可以马上设置它。

public func getThumbnailImage(forPage page: String, imageView : UIImageView) 
{
  ...
  ...
         if let imageData = try? Data(contentsOf: url) {
            DispatchQueue.main.async {
              if let image = UIImage(data: imageData) {
                imageView.image = image                                 
              }
            }
         }
}