Отображение изображения с URL, Swift 4.2 - PullRequest
0 голосов
/ 11 ноября 2018

Я довольно приличный разработчик Objective C, и сейчас я изучаю Swift (что я нахожу довольно трудным, не только из-за новых концепций, таких как опционные, но также и потому, что Swift постоянно развивается, и большая часть доступные учебники сильно устарели).

В настоящее время я пытаюсь проанализировать JSON из URL-адреса в NSDictionary и затем использовать одно из его значений для отображения изображения (которое также является URL-адресом). Примерно так:


URL -> NSDictionary -> init UIImage из URL -> отображать UIImage в UIImageView


Это довольно просто в Задаче 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];

Приведенный выше фрагмент кода возвращает мне стандартный NSDictionary, в котором я могу обратиться к ключу "url", чтобы получить адрес изображения, которое я хочу отобразить:


"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];

Теперь я в основном пытаюсь повторить приведенный выше код Objective C в Swift, но постоянно сталкиваюсь со стенами. Я перепробовал несколько учебных пособий (одно из которых на самом деле делало то же самое: отображать изображение 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

Если это не так уж и много, я также хотел бы попросить подробные описания вместе с кодом, потому что я хотел бы, чтобы ответ был единственным всеобъемлющим "учебным пособием" для этой задачи, которое, я считаю, в настоящее время нигде не доступно.

Спасибо!

Ответы [ 8 ]

0 голосов
/ 28 марта 2019

Пользовательская SDWebImage сторонняя библиотека для асинхронного загрузчика изображений

import SDWebImage

imageView.sd_setImage(with: URL(string: url), placeholderImage: UIImage(named: "placeholder.png"))
0 голосов
/ 13 ноября 2018

Я только что изложил ответ Вадиана, разделив некоторые проблемы, чтобы четко понять основы.Его ответа должно быть достаточно.

Сначала вы должны построить свою структуру.Это будет представлять структуру JSON, которую вы извлекли из веб-службы.

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

Затем сделайте запрос методов.Я обычно создаю отдельный файл для него.Теперь Вадиан упомянул об обработчиках завершения.Они представлены избегающими замыканиями.Здесь 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 
           }
      }
  }

Это не лучшие практики, так как я все еще учусь, поэтомуВсе средства изучите темы, особенно замыкания, параллелизм ios и компоненты URLC в документации Apple :)

0 голосов
/ 28 марта 2019

Вы можете использовать это расширение

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
}
0 голосов
/ 12 ноября 2018

Создайте расширение UIIimageView и следующий код

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!)
0 голосов
/ 12 ноября 2018

Сначала добавьте модуль в Podfile под "Аламофире", стручок 'AlamofireImage' Вы можете проверить эту ссылку для установки pods => https://cocoapods.org/pods/AlamofireImage

// Используйте эту функцию для загрузки изображения из URL в imageview

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

0 голосов
/ 12 ноября 2018

Поскольку загрузка изображений - тривиальная и в то же время задача, которая может быть реализована множеством различных способов, я бы порекомендовал вам не «изобретать велосипед» и взглянуть на библиотеку загрузки изображений, такую ​​как Nuke , поскольку он уже охватывает большинство случаев, которые могут вам понадобиться в процессе разработки.

Позволяет асинхронно загружать и отображать изображение на вашем экране, используя простой 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)
0 голосов
/ 12 ноября 2018

вам нужно конвертировать URL в строку и данные для добавления в viewview

let imageURL:URL=URL(string: YourImageURL)!
let data=NSData(contentsOf: imageURL)
Yourimage.image=UIImage(data: data! as Data)
0 голосов
/ 11 ноября 2018

Прежде всего, я уверен, что через полгода вы найдете Objective-C очень сложным и трудным. ?

Во-вторых, даже ваш код ObjC не рекомендуется. Не загружайте данные с удаленного URL-адреса синхронным методом Data(contentsOf. Независимо от языка используйте асинхронный способ, например (NS)URLSession.

И не используйте типы коллекции Foundation NSArray и NSDictionary в Swift. По сути, вообще не используйте NS... классы, если есть нативный аналог Swift.

В Swift 4 вы можете легко декодировать JSON с протоколом Decodable непосредственно в (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()
...