2016-10-25 2 views
3

Я пытаюсь использовать многопоточность в Swift для извлечения и отображения изображения в контроллере представления. Однако функция, которую я использую для извлечения изображения, находится в отдельной модели. Я мог бы просто скопировать и вставить функцию в контроллер представления, но я буду повторно использовать эту функцию несколько раз и предпочту оставить ее отдельно от конкретного контроллера представления.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 
      } 

     } 

    } 

Я использую словарь (wikimediaPageTitleDict) из списка свойств, чтобы связать строку с правой страницы википедии (allergy.0 что строка)

Вся помощь оценивается. Спасибо!

+0

также JSON выше, относится к SwiftyJSON (https: // GitHub.com/SwiftyJSON/SwiftyJSON) –

+1

Просто голова, вы можете связать свои 'if let' в том же состоянии, чтобы избежать пирамиды смерти. – JAL

ответ

1

Оба AlamofireImage и SDWebImage сделать это для вас, и они проходят 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 
     } 
    } 
+0

Хорошо, я забыл, что Alamofire построил загрузку асинхронного изображения. Это, безусловно, самый простой и лучший ответ, который вы уже используете Alamofire –

+0

спасибо! Я закончил использовать Alamofire, но демо-код был действительно полезен для меня, чтобы понять, как это обычно должно быть сделано. –

+0

также ценят структуру охраны, не понимал, что я мог бы это сделать, но выглядит более чистым –

0

Если вы используете асинхронный код, вы не можете вернуть результат из своей функции. Это не так, как работает async. Асинхронный вызов функции ставит в очередь результат, который будет получен позже. Затем функция возвращается до получения результатов.

Что вы обычно делаете с асинхронными функциями - это передать в блок завершения, который вызывается, когда доступны результаты. Вы можете передать закрытие, которое устанавливает недавно загруженное изображение в соответствующее изображение в вашем контроллере вида, для пример.

См. Мой ответ на эту тему.

Storing values in completionHandlers - Swift

я дам рабочий пример функции асинхронной, который принимает обработчик завершения.

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         
       } 
      } 
     } 
} 
Смежные вопросы