Cargando/Descargando imagen desde URL en Swift
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?
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")
(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