Cargando/Descargando imagen desde URL en Swift

Resuelto QuentR asked hace 54 años • 39 respuestas

Me gustaría cargar una imagen desde una URL en mi aplicación, así que primero probé con Objective-C y funcionó, sin embargo, con Swift, tengo un error de compilación:

'imageWithData' no está disponible: use la construcción del objeto 'UIImage(data:)'

Mi función:

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

En 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.urltest.fr/assets/img/iOS%20icon's%20Style/ios8.png";
 }

¿Alguien puede explicarme por qué imageWithData:no funciona con Swift y cómo puedo resolver el problema?

QuentR avatar Jan 01 '70 08:01 QuentR
Aceptado

Xcode 8 o posterior • Swift 3 o posterior

Sincrónicamente:

if let filePath = Bundle.main.path(forResource: "imageName", ofType: "jpg"), let image = UIImage(contentsOfFile: filePath) {
    imageView.contentMode = .scaleAspectFit
    imageView.image = image
}

Asincrónicamente:

Cree un método con un controlador de finalización para obtener los datos de la imagen de su URL

func getData(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ()) {
    URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}

Cree un método para descargar la imagen (inicie la tarea)

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

Uso:

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

Extensión :

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

Uso:

imageView.downloaded(from: "https://cdn.arstechnica.net/wp-content/uploads/2018/06/macOS-Mojave-Dynamic-Wallpaper-transition.jpg")
Leo Dabus avatar Dec 30 '2014 20:12 Leo Dabus

(Actualización de Swift 4) Para responder la pregunta original directamente, aquí está el equivalente rápido del fragmento de Objective-C publicado.

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

DESCARGO DE RESPONSABILIDAD:

Es importante tener en cuenta que el Data(contentsOf:)método descargará el contenido de la URL de forma sincrónica en el mismo hilo en el que se ejecuta el código, así que no invoque esto en el hilo principal de su aplicación.

Una forma sencilla de hacer que el mismo código se ejecute de forma asincrónica, sin bloquear la interfaz de usuario, es mediante 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!)
    }
}

Dicho esto, en las aplicaciones de la vida real, si desea tener la mejor experiencia de usuario y evitar descargas múltiples de la misma imagen, es posible que también desee no solo descargarlas, sino también almacenarlas en caché. Ya existen bastantes bibliotecas que hacen esto de manera muy sencilla y todas son realmente fáciles de usar. Personalmente recomiendo 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) 

También funciona con SwiftUI:

var body: some View {
    KFImage(URL(string: "https://example.com/image.png")!)
}

Y eso es

Lucas Eduardo avatar Dec 17 '2014 02:12 Lucas Eduardo