Carga / descarga de imágenes desde URL en Swift

415

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 funcion:

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

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

QuentR
fuente
3
Prueba estoimageURL.image = UIImage(data: myDataVar)
aleclarson
Perfecto funcionó! Gracias Sin embargo, no sé por qué este método funciona en el Objetivo C, y no en Swift ... Extraño
QuentR
¡Cuando tenga problemas con una clase de Cocoa, intente CMD + haciendo clic en el nombre de la clase y debería poder ver la interfaz Swift para la clase!
aleclarson
3
if let let = NSURL (string: "imageurl") {if let data = NSData (contentsOfURL: url) {imageView.image = UIImage (data: data)}}
Hardik Bar
2
@Leo Dabus Esta pregunta no es específica de Swift 2. Deje de agregarle esa etiqueta.
Mick MacCallum

Respuestas:

771

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 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 (comience 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")
        DispatchQueue.main.async() { [weak self] in
            self?.imageView.image = UIImage(data: data)
        }
    }
}

Uso:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    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: UIViewContentMode = .scaleAspectFit) {  // for swift 4.2 syntax just use ===> mode: UIView.ContentMode
        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: UIViewContentMode = .scaleAspectFit) {  // for swift 4.2 syntax just use ===> mode: UIView.ContentMode
        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
fuente
13
Solo una nota al margen aquí, debe establecer un objeto asociado; De lo contrario, podría cargar imágenes una encima de la otra. Por ejemplo, en un lugar UITableViewdonde una celda muestra una imagen y la imagen se actualiza cuando se devuelve la celda en cola. Si el proceso n. ° 1 demora más que el proceso n. ° 2, el proceso n. ° 2 mostrará su imagen y luego se actualizará con el proceso n. ° 1 aunque esa imagen ya no sea válida para el usuario.
Paul Peelen
1
Esto me resolvió mi problema. Cuando intento cargar una imagen con url en la vista de tabla, la vista de desplazamiento realmente no responde cuando se desplaza hacia arriba o hacia abajo. Usé la extensión UIImageView y resolvió mi problema. Gracias.
JengGe Chao
1
@LeoDabus ok gracias. y gracias por responder tantas preguntas sobre SO! eres como un profesor de swift y iOS. :)
Crashalot
2
@LeoDabus, ¿cómo es que usaste dataTaskWithURL en lugar de downloadTaskWithURL?
Crashalot
3
El primero se descarga en la memoria, el segundo en un archivo
Leo Dabus el
350

(Actualización de Swift 4) Para responder directamente a la pregunta original, 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á los contenidos de la url sincrónicamente en el mismo hilo en el que se está ejecutando el código, así que no invoque esto en el hilo principal de su aplicación.

Una manera fácil de hacer que el mismo código se ejecute de forma asincrónica, sin bloquear la interfaz de usuario, es mediante el uso de 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 múltiples descargas de la misma imagen, es posible que también desee tenerlas no solo descargadas, sino también almacenadas en caché. Ya hay bastantes bibliotecas que lo hacen sin problemas y todas son realmente fáciles de usar. Yo 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) 

Y eso es

Lucas Eduardo
fuente
12
"Fácilmente" es subjetivo, y los programadores novatos no esperarán este comportamiento no deseado, pero copie / pegue este recorte tal como está. ¿Quizás puedas actualizar tu respuesta?
Orlin Georgiev
13
Todavía no estoy de acuerdo. Algunas respuestas deben ser simples, a muchas personas que vienen aquí les gusta copiar y pegar pequeños fragmentos de código. Acabo de traducir a Swift el código del objetivo C de la pregunta original, todo lo demás además de eso es una ventaja. Y, por cierto, ya hay una respuesta en esta misma pregunta que proporciona este tipo de información adicional, que tiene menos sentido duplicar información.
Lucas Eduardo
1
@User simplemente reemplaza el image.urlpor cualquier url de cadena, codificado o no :)
Lucas Eduardo
1
@IanWarburton Por supuesto, puedes usar lo que quieras :). 1) Esta respuesta se basa en la pregunta original, que utilizaba el mismo enfoque en el objetivo-C, por lo que simplemente ayudé en la "traducción" a acelerar. 2) No tiene sentido eliminar este enfoque y poner el URLSession.dataTaskporque muchas otras respuestas aquí ya muestran cómo hacerlo, es mejor mantener abiertas las diferentes opciones.
Lucas Eduardo
2
Heyo, el marco sugerido, martín pescador, como se indica en la respuesta, hará todo el trabajo pesado. ¡Gracias por la sugerencia!
Kilmazing
68

Si solo desea cargar la imagen (¡Asincrónicamente!) , Simplemente agregue esta pequeña extensión a su código rápido:

extension UIImageView {
    public func imageFromUrl(urlString: String) {
        if let url = NSURL(string: urlString) {
            let request = NSURLRequest(URL: url)
            NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {
                (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in
                if let imageData = data as NSData? {
                    self.image = UIImage(data: imageData)
                }
            }
        }
    }
}

Y úsalo de esta manera:

myImageView.imageFromUrl("https://robohash.org/123.png")
skywinder
fuente
1
No está apareciendo, y creo que porque hay una necesidad de GCD. ¿Cómo actualizo?
jo3birdtalk
@ jo3birdtalk no es un problema de GCD. Verifique sus enlaces con la vista.
skywinder
66
NSURLConnection está en desuso en iOS 9 y así sucesivamente. Utilice NSURLSession intead.
muhasturk
2
sendAsynchronousRequest está en desuso en iOS 9
Crashalot el
Gracias skywinder, estoy usando este método para descargar imágenes de la matriz. Quiero que cuando el usuario presione el cancelbotón deje de descargar. ¿Tienes alguna idea de cómo puedo hacer esto con este método? Necesito agregar la funcionalidad de cancelar.
ZAFAR007
44

Swift 2.2 || Xcode 7.3

¡ Obtuve resultados asombrosos ! con la biblioteca rápida AlamofireImage

Proporciona múltiples funciones como:

  • Descarga asincrónica
  • Caché de imagen de purga automática si se producen advertencias de memoria para la aplicación
  • Caché de URL de imagen
  • Almacenamiento en caché de imagen
  • Evitar descargas duplicadas

y muy fácil de implementar para tu aplicación

Paso.1 Instalar pods


Alamofire 3.3.x

pod 'Alamofire'

AlamofireImage 2.4.x

pod 'AlamofireImage'

Paso.2 importar y usar

import Alamofire
import AlamofireImage

let downloadURL = NSURL(string: "http://cdn.sstatic.net/Sites/stackoverflow/company/Img/photos/big/6.jpg?v=f4b7c5fee820")!
imageView.af_setImageWithURL(downloadURL)

¡¡Eso es!! se encargará de todo


Muchas gracias a los muchachos de Alamofire , por hacer que la vida de iDevelopers sea fácil;)

swiftBoy
fuente
8
Swift 3: foregroundImage.af_setImage (withURL: downloadURL como URL)
thejuki
28

Xcode 8Swift 3

¡La respuesta de Leo Dabus es asombrosa! Solo quería proporcionar una solución de función todo en uno:

let url = URL(string: 
    "http://www.apple.com/euro/ios/ios8/a/generic/images/og.png")

let task = URLSession.shared.dataTask(with: url!) { data, response, error in
    guard let data = data, error == nil else { return }

    DispatchQueue.main.async() {    // execute on main thread
        self.imageView.image = UIImage(data: data)
    }
}

task.resume()
Mark Moeykens
fuente
3
Mark, probablemente significaba "asíncrono" al final allí
Fattie
16

Envolví el código de las mejores respuestas a la pregunta en una sola clase reutilizable que extiende UIImageView, para que pueda usar directamente UIImageViews de carga asincrónica en su guión gráfico (o crearlos desde el código).

Aquí está mi clase:

import Foundation
import UIKit

class UIImageViewAsync :UIImageView
{

    override init()
    {
        super.init(frame: CGRect())
    }

    override init(frame:CGRect)
    {
        super.init(frame:frame)
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    func getDataFromUrl(url:String, completion: ((data: NSData?) -> Void)) {
        NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) { (data, response, error) in
            completion(data: NSData(data: data))
        }.resume()
    }

    func downloadImage(url:String){
        getDataFromUrl(url) { data in
            dispatch_async(dispatch_get_main_queue()) {
                self.contentMode = UIViewContentMode.ScaleAspectFill
                self.image = UIImage(data: data!)
            }
        }
    }
}

y aquí está cómo usarlo:

imageView.downloadImage("http://www.image-server.com/myImage.jpg")
datayeah
fuente
2
¿Realmente necesitamos esos inicios anulados? ¿No se heredan los inits de todos modos? Parece que estamos anulando aquí solo para llamar super en sí mismo, lo que me parece redundante.
NYC Tech Engineer
16

Swift 4: ::

Esto mostrará el cargador mientras carga la imagen. Puede usar NSCache que almacena la imagen temporalmente

let imageCache = NSCache<NSString, UIImage>()
extension UIImageView {
    func loadImageUsingCache(withUrl urlString : String) {
        let url = URL(string: urlString)
        if url == nil {return}
        self.image = nil

        // check cached image
        if let cachedImage = imageCache.object(forKey: urlString as NSString)  {
            self.image = cachedImage
            return
        }

        let activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView.init(activityIndicatorStyle: .gray)
        addSubview(activityIndicator)
        activityIndicator.startAnimating()
        activityIndicator.center = self.center

        // if not, download image from url
        URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
            if error != nil {
                print(error!)
                return
            }

            DispatchQueue.main.async {
                if let image = UIImage(data: data!) {
                    imageCache.setObject(image, forKey: urlString as NSString)
                    self.image = image
                    activityIndicator.removeFromSuperview()
                }
            }

        }).resume()
    }
}

Uso:-

truckImageView.loadImageUsingCache(withUrl: currentTruck.logoString)
Jack
fuente
1
Funciona. Simplemente copie la eliminación de ActivityIndicator en caso de error también.
djdance
1
Muy útil para mi proyecto.
Partikles
13
let url = NSURL.URLWithString("http://live-wallpaper.net/iphone/img/app/i/p/iphone-4s-wallpapers-mobile-backgrounds-dark_2466f886de3472ef1fa968033f1da3e1_raw_1087fae1932cec8837695934b7eb1250_raw.jpg");
var err: NSError?
var imageData :NSData = NSData.dataWithContentsOfURL(url,options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &err)
var bgImage = UIImage(data:imageData)
usuario3763002
fuente
55
fatal error: unexpectedly found nil while unwrapping an Optional value
Usuario
¿Alguna posibilidad de que puedas escribir algo asincrónico?
Jed Grant
@JedGrant puedes usar dispatch_async para entrar en otro hilo y una devolución de llamada
Matej
Tuve que desenvolver el NSData con un "!" (tenga en cuenta! al final) para que funcione así: var imageData: NSData = NSData (contentsOfURL: url, opciones: NSDataReadingOptions.DataReadingMappedIfSafe, error: & err)!
botbot
13

FYI: Para swift-2.0 Xcode7.0 beta2

extension UIImageView {
    public func imageFromUrl(urlString: String) {
        if let url = NSURL(string: urlString) {
            let request = NSURLRequest(URL: url)
            NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {
            (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in
                self.image = UIImage(data: data!)
            }
        }
    }
}
katopz
fuente
55
NSURLConnection está depracado. Tienes que usar NSURLSession. Es mejor cuando codifica con Swift 2.0 y Xcode 7
BilalReffas
11

Swift 3 con manejo de errores

let url = URL(string: arr[indexPath.row] as! String)
if url != nil {
    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 {
            if data != nil {
                cell.imgView.image = UIImage(data:data!)
            }else{
                cell.imgView.image = UIImage(named: "default.png")
            }
        }
    }
}

Con extensión

extension UIImageView {

    func setCustomImage(_ imgURLString: String?) {
        guard let imageURLString = imgURLString else {
            self.image = UIImage(named: "default.png")
            return
        }
        DispatchQueue.global().async { [weak self] in
            let data = try? Data(contentsOf: URL(string: imageURLString)!)
            DispatchQueue.main.async {
                self?.image = data != nil ? UIImage(data: data!) : UIImage(named: "default.png")
            }
        }
    }
}

Uso de extensión

myImageView. setCustomImage("url")

Con soporte de caché

let imageCache = NSCache<NSString, UIImage>()

extension UIImageView {

    func loadImageUsingCacheWithURLString(_ URLString: String, placeHolder: UIImage?) {

        self.image = nil
        if let cachedImage = imageCache.object(forKey: NSString(string: URLString)) {
            self.image = cachedImage
            return
        }

        if let url = URL(string: URLString) {
            URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in

                //print("RESPONSE FROM API: \(response)")
                if error != nil {
                    print("ERROR LOADING IMAGES FROM URL: \(String(describing: error))")
                    DispatchQueue.main.async { [weak self] in
                        self?.image = placeHolder
                    }
                    return
                }
                DispatchQueue.main.async { [weak self] in
                    if let data = data {
                        if let downloadedImage = UIImage(data: data) {
                            imageCache.setObject(downloadedImage, forKey: NSString(string: URLString))
                            self?.image = downloadedImage
                        }
                    }
                }
            }).resume()
        }
    }
}
Manee ios
fuente
9

Swift 4: un cargador simple para imágenes pequeñas (ej .: miniaturas) que usa NSCache y siempre se ejecuta en el hilo principal:

class ImageLoader {

  private static let cache = NSCache<NSString, NSData>()

  class func image(for url: URL, completionHandler: @escaping(_ image: UIImage?) -> ()) {

    DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async {

      if let data = self.cache.object(forKey: url.absoluteString as NSString) {
        DispatchQueue.main.async { completionHandler(UIImage(data: data as Data)) }
        return
      }

      guard let data = NSData(contentsOf: url) else {
        DispatchQueue.main.async { completionHandler(nil) }
        return
      }

      self.cache.setObject(data, forKey: url.absoluteString as NSString)
      DispatchQueue.main.async { completionHandler(UIImage(data: data as Data)) }
    }
  }

}

Uso:

ImageLoader.image(for: imageURL) { image in
  self.imageView.image = image
}
fethica
fuente
¿Por qué usar singleton con struct? esto hará que su clase sea inmutable, lo que puede causar problemas con su NSCache
XcodeNOOB
Gracias por señalar esto, lo acabo de cambiar class.
fethica
1
private let cache me estaba dando un error, así que lo cambié a static y funcionó. Gracias
Hammad Tariq
7

Querrás hacer:

UIImage(data: data)

En Swift, han reemplazado la mayoría de los métodos de fábrica de Objective C con constructores regulares.

Ver:

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-XID_26

joshstaiger
fuente
2
Técnicamente mapeado, no reemplazado. Si crea su propio método +(instancetype)[MyThing thingWithOtherThing:], también lo llamaría como MyThing(otherThing: ...)en Swift.
Brian Nickel
7

Swift 2 con controlador de error y encabezado de solicitud personalizado

Simplemente agregue la extensión a UIImageView:

extension UIImageView {
    public func imageFromUrl(urlString: String) {
        if let url = NSURL(string: urlString) {
            let request = NSMutableURLRequest(URL: url)
            request.setValue("<YOUR_HEADER_VALUE>", forHTTPHeaderField: "<YOUR_HEADER_KEY>")
            NSURLSession.sharedSession().dataTaskWithRequest(request) {
                (data, response, error) in
                guard let data = data where error == nil else{
                    NSLog("Image download error: \(error)")
                    return
                }

                if let httpResponse = response as? NSHTTPURLResponse{
                    if httpResponse.statusCode > 400 {
                        let errorMsg = NSString(data: data, encoding: NSUTF8StringEncoding)
                        NSLog("Image download error, statusCode: \(httpResponse.statusCode), error: \(errorMsg!)")
                        return
                    }
                }

            dispatch_async(dispatch_get_main_queue(), {
                NSLog("Image download success")
                self.image = UIImage(data: data)
            })
            }.resume()
        }
    }
}

Y luego, usa la nueva imageFromUrl(urlString: String)imagen para descargar

Uso:

imageView.imageFromUrl("https://i.imgur.com/ONaprQV.png")
Cody
fuente
en Swift 3, sigo recibiendo este error. Cuál es el problema ? En esta línea "" "URLSession.shared.dataTask (con: url) {" "" "" "" No se puede invocar 'dataTask' con una lista de argumentos de tipo '(con: URL, (Datos ?, URLResponse ?, ¿Error? ) -> Void) '
Aymen BRomdhane
7

Swift 4

Este método descargará una imagen de un sitio web de forma asíncrona y la almacenará en caché:

    func getImageFromWeb(_ urlString: String, closure: @escaping (UIImage?) -> ()) {
        guard let url = URL(string: urlString) else {
return closure(nil)
        }
        let task = URLSession(configuration: .default).dataTask(with: url) { (data, response, error) in
            guard error == nil else {
                print("error: \(String(describing: error))")
                return closure(nil)
            }
            guard response != nil else {
                print("no response")
                return closure(nil)
            }
            guard data != nil else {
                print("no data")
                return closure(nil)
            }
            DispatchQueue.main.async {
                closure(UIImage(data: data!))
            }
        }; task.resume()
    }

En uso:

    getImageFromWeb("http://www.apple.com/euro/ios/ios8/a/generic/images/og.png") { (image) in
        if let image = image {
            let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
            imageView.image = image
            self.view.addSubview(imageView)
        } // if you use an Else statement, it will be in background
    }
Poli
fuente
¿Cómo es el almacenamiento en caché? ¿Y por cuánto tiempo?
Ramis
Parece estar almacenado permanentemente, lo cual es extraño, está almacenado en la carpeta Biblioteca> Cachés. Use print (NSHomeDirectory ()) para llegar a esta ubicación en su computadora cuando se ejecuta en el simulador.
Bobby
5

Swift 2.0:

1)

if let url = NSURL(string: "http://etc...") {
    if let data = NSData(contentsOfURL: url) {
        imageURL.image = UIImage(data: data)
    }        
}

O

imageURL.image =
    NSURL(string: "http:// image name...")
    .flatMap { NSData(contentsOfURL: $0) }
    .flatMap { UIImage(data: $0) }

2) Agregue este método a VC o Extension.

func load_image(urlString:String)
{   let imgURL: NSURL = NSURL(string: urlString)!
    let request: NSURLRequest = NSURLRequest(URL: imgURL)

    NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) { (response: NSURLResponse?, data: NSData?, error: NSError?) in

        if error == nil {
            self.image_element.image = UIImage(data: data)
        }
    }
}

Uso:

self.load_image(" url strig here")
AG
fuente
sendAsynchronousRequest está en desuso en iOS 9
Crashalot el
5

Kingfisher es una de las mejores bibliotecas para cargar imágenes en URL.

URL de Github: https://github.com/onevcat/Kingfisher

// If you want to use Activity Indicator.
imageview_pic.kf.indicatorType = .activity
imageview_pic.kf.setImage(with: URL(string: "Give your url string"))

// If you want to use custom placeholder image.
imageview_pic.kf.setImage(with: URL(string: "Give your url string"), placeholder: UIImage(named: "placeholder image name"), options: nil, progressBlock: nil, completionHandler: nil)
Kamani Jasmin
fuente
5

Aquí está el código de trabajo para cargar / descargar imágenes desde la URL. NSCache automáticamente y muestra la imagen del marcador de posición antes de descargar y cargar la imagen real (código Swift 4).

func NKPlaceholderImage(image:UIImage?, imageView:UIImageView?,imgUrl:String,compate:@escaping (UIImage?) -> Void){

    if image != nil && imageView != nil {
        imageView!.image = image!
    }

    var urlcatch = imgUrl.replacingOccurrences(of: "/", with: "#")
    let documentpath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
    urlcatch = documentpath + "/" + "\(urlcatch)"

    let image = UIImage(contentsOfFile:urlcatch)
    if image != nil && imageView != nil
    {
        imageView!.image = image!
        compate(image)

    }else{

        if let url = URL(string: imgUrl){

            DispatchQueue.global(qos: .background).async {
                () -> Void in
                let imgdata = NSData(contentsOf: url)
                DispatchQueue.main.async {
                    () -> Void in
                    imgdata?.write(toFile: urlcatch, atomically: true)
                    let image = UIImage(contentsOfFile:urlcatch)
                    compate(image)
                    if image != nil  {
                        if imageView != nil  {
                            imageView!.image = image!
                        }
                    }
                }
            }
        }
    }
}

Usar así:

// Here imgPicture = your imageView
// UIImage(named: "placeholder") is Display image brfore download and load actual image. 

NKPlaceholderImage(image: UIImage(named: "placeholder"), imageView: imgPicture, imgUrl: "Put Here your server image Url Sting") { (image) in }
Nikunj Kumbhani
fuente
1
Lo único que funcionó conmigo. mi problema era que estaba implementando un reproductor de música y quería que la imagen se cargara en la notificación cuando el teléfono también está cerrado y el controlador toma el tipo de UIImage, así que tuve que usar este método.
JhonnyTawk
4

Un método para obtener una imagen segura y que funcione con Swift 2.0 y X-Code 7.1:

static func imageForImageURLString(imageURLString: String, completion: (image: UIImage?, success: Bool) -> Void) {
    guard let url = NSURL(string: imageURLString),
        let data = NSData(contentsOfURL: url),
        let image = UIImage(data: data)
        else { 
            completion(image: nil, success: false); 
            return 
       }

    completion(image: image, success: true)
}

Entonces llamarías a este método así:

imageForImageURLString(imageString) { (image, success) -> Void in
        if success {
            guard let image = image 
                 else { return } // Error handling here 
            // You now have the image. 
         } else {
            // Error handling here.
        }
    }

Si está actualizando la vista con la imagen, deberá usarla después de "if success {":

    dispatch_async(dispatch_get_main_queue()) { () -> Void in
         guard let image = image 
              else { return } // Error handling here 
         // You now have the image. Use the image to update the view or anything UI related here
         // Reload the view, so the image appears
    }

La razón por la que se necesita esta última parte si está utilizando la imagen en la interfaz de usuario es porque las llamadas de red toman tiempo. Si intenta actualizar la interfaz de usuario utilizando la imagen sin llamar a dispatch_async como se indicó anteriormente, la computadora buscará la imagen mientras la imagen aún se está recuperando, encontrará que no hay imagen (todavía) y continuará como si no hubiera imagen encontró. Poner su código dentro de un cierre de finalización de dispatch_async le dice a la computadora: "Vaya, obtenga esta imagen y cuando haya terminado, luego complete este código". De esa manera, tendrá la imagen cuando se llame al código y las cosas funcionarán bien.

Parche de ben
fuente
4

Si está buscando una implementación muy muy simple. (Esto funcionó para mí en Swift 2)

 let imageURL = NSURL(string: "https://farm2.staticflickr.com/1591/26078338233_d1466b7da2_m.jpg")
 let imagedData = NSData(contentsOfURL: imageURL!)!
 imageView?.image = UIImage(data: imagedData)

Implementé dentro de una vista de tabla con una celda personalizada que solo tiene una imagen

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{

        let cell = tableView.dequeueReusableCellWithIdentifier("theCell", forIndexPath: indexPath) as! customTableViewCell

        let imageURL = NSURL(string: "https://farm2.staticflickr.com/1591/26078338233_d1466b7da2_m.jpg")

        let imagedData = NSData(contentsOfURL: imageURL!)!

        cell.imageView?.image = UIImage(data: imagedData)

        return cell

    }
Naishta
fuente
tienes razón, eso es porque la descarga y la compresión están ocurriendo en el hilo principal. Lo ideal es que realice estos 2 pasos de forma asíncrona en un subproceso en segundo plano, puede estar utilizando la cola global dispatch_async
Naishta
Sí, mediante el uso de subprocesos en segundo plano podemos optimizar la velocidad de descarga y, si es necesario, podemos cambiar la lógica en lugar de esto, podemos usar sdWebImage u otro marco.
Gourav Joshi
2

Recomiendo usar la biblioteca Kingfisher para descargar imágenes de forma asincrónica. La mejor parte sobre el uso de Kingfisher es que almacena en caché todas las imágenes descargadas de forma predeterminada con la URL de la imagen como id. La próxima vez que solicite descargar una imagen con esa URL en particular, la cargará de la memoria caché.

Uso:

newsImage.kf.setImage(with: imageUrl!, placeholder: nil, options: nil, progressBlock: nil, completionHandler: { (image, error, cacheType, imageUrl) in
                if error == nil{
                    self.activityIndicator.stopAnimating()
                }else if error != nil{
                    self.activityIndicator.stopAnimating()
                }
            })
Raj Salla
fuente
2

Puedes usar pod SDWebImagepara lograr lo mismo. Es fácil de usar. Puedes obtener documentación aquí SDWebImage

Aquí está el código de ejemplo

self.yourImage.sd_setImage(with: NSURL(string: StrUrl as String ) as URL!, placeholderImage: placeholderImage, options: SDWebImageOptions(rawValue: 0), completed: { (image, error, cacheType, imageURL) in
                if( error != nil)
                {
                    print("Error while displaying image" , (error?.localizedDescription)! as String)
                }
            })
Shruti Thombre
fuente
2

rápido 5

extension UIImageView {
    func load(url: URL) {
        DispatchQueue.global().async { [weak self] in
            if let data = try? Data(contentsOf: url) {
                if let image = UIImage(data: data) {
                    DispatchQueue.main.async {
                        self?.image = image
                    }
                }
            }
        }
    }
}

Para usar

override func awakeFromNib() {
    super.awakeFromNib()
    imgView.load(url: "<imageURLHere>")
}
Amjaber
fuente
1

¡Lo único que falta es un!

let url = NSURL.URLWithString("http://live-wallpaper.net/iphone/img/app/i/p/iphone-4s-wallpapers-mobile-backgrounds-dark_2466f886de3472ef1fa968033f1da3e1_raw_1087fae1932cec8837695934b7eb1250_raw.jpg");
var err: NSError?
var imageData :NSData = NSData.dataWithContentsOfURL(url!,options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &err)
var bgImage = UIImage(data:imageData!)
Simon Jensen
fuente
Perdón por la respuesta tardía, ¿podría ser más preciso sobre los errores que obtiene?
Simon Jensen
1

Respuesta Swift 2.x que descarga la imagen al archivo (a diferencia de la respuesta de Leo Dabus, que almacena la imagen en la memoria). Basado en la respuesta de Leo Dabus y la respuesta de Rob de Obtener los datos de NSURLSession DownloadTaskWithRequest del controlador de finalización :

    // Set download vars
    let downloadURL = NSURL() // URL to download from
    let localFilename = "foobar.png" // Filename for storing locally 

    // Create download request
    let task = NSURLSession.sharedSession().downloadTaskWithURL(downloadURL) { location, response, error in
        guard location != nil && error == nil else {
            print("Error downloading message: \(error)")
            return
        }

        // If here, no errors so save message to permanent location
        let fileManager = NSFileManager.defaultManager()
        do {
            let documents = try fileManager.URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
            let fileURL = documents.URLByAppendingPathComponent(localFilename)
            try fileManager.moveItemAtURL(location!, toURL: fileURL)
            self.doFileDownloaded(fileURL, localFilename: localFilename)
            print("Downloaded message @ \(localFilename)")
        } catch {
            print("Error downloading message: \(error)")
        }
    }

    // Start download
    print("Starting download @ \(downloadURL)")
    task.resume()


// Helper function called after file successfully downloaded
private func doFileDownloaded(fileURL: NSURL, localFilename: String) {

    // Do stuff with downloaded image

}
Crashalot
fuente
1

Swift 4.1 He creado una función solo pasa la url de la imagen, la clave de caché después de que se genera la imagen la configuró en el bloque de finalización.

   class NetworkManager: NSObject {

  private var imageQueue = OperationQueue()
  private var imageCache = NSCache<AnyObject, AnyObject>()

  func downloadImageWithUrl(imageUrl: String, cacheKey: String, completionBlock: @escaping (_ image: UIImage?)-> Void) {

    let downloadedImage = imageCache.object(forKey: cacheKey as AnyObject)
    if let  _ = downloadedImage as? UIImage {
      completionBlock(downloadedImage as? UIImage)
    } else {
      let blockOperation = BlockOperation()
      blockOperation.addExecutionBlock({
        let url = URL(string: imageUrl)
        do {
          let data = try Data(contentsOf: url!)
          let newImage = UIImage(data: data)
          if newImage != nil {
            self.imageCache.setObject(newImage!, forKey: cacheKey as AnyObject)
            self.runOnMainThread {
              completionBlock(newImage)
            }
          } else {
            completionBlock(nil)
          }
        } catch {
          completionBlock(nil)
        }
      })
      self.imageQueue.addOperation(blockOperation)
      blockOperation.completionBlock = {
        print("Image downloaded \(cacheKey)")
      }
    }
  }
}
extension NetworkManager {
  fileprivate func runOnMainThread(block:@escaping ()->Void) {
    if Thread.isMainThread {
      block()
    } else {
      let mainQueue = OperationQueue.main
      mainQueue.addOperation({
        block()
      })
    }
  }
}
Gurjinder Singh
fuente
1
class func downloadImageFromUrl(with urlStr: String, andCompletionHandler:@escaping (_ result:Bool) -> Void) {
        guard let url = URL(string: urlStr) else {
            andCompletionHandler(false)
            return
        }
        DispatchQueue.global(qos: .background).async {
            URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) -> Void in
                if error == nil {
                    let httpURLResponse = response as? HTTPURLResponse
                    Utils.print( "status code ID : \(String(describing: httpURLResponse?.statusCode))")
                    if httpURLResponse?.statusCode == 200 {
                        if let data = data {
                            if let image = UIImage(data: data) {
                                ImageCaching.sharedInterface().setImage(image, withID: url.absoluteString as NSString)
                                DispatchQueue.main.async {
                                    andCompletionHandler(true)
                                }
                            }else {
                                andCompletionHandler(false)
                            }
                        }else {
                            andCompletionHandler(false)
                        }
                    }else {
                        andCompletionHandler(false)
                    }
                }else {
                    andCompletionHandler(false)
                }
            }).resume()
        }
    }

He creado una función de clase simple en mi Utils.swiftclase para llamar a ese método al que simplemente puede acceder classname.methodnamey sus imágenes se guardan en NSCache usando la ImageCaching.swiftclase

Utils.downloadImageFromUrl(with: URL, andCompletionHandler: { (isDownloaded) in
                            if isDownloaded {
                                if  let image = ImageCaching.sharedInterface().getImage(URL as NSString) {
                                    self.btnTeam.setBackgroundImage(image, for: .normal)
                                }
                            }else {
                                DispatchQueue.main.async {
                                    self.btnTeam.setBackgroundImage(#imageLiteral(resourceName: "com"), for: .normal)
                                }
                            }
                        })

Feliz codificación. Salud:)

Vishal16
fuente
1

Swift 4.2 y AlamofireImage

Si usar una biblioteca no es un problema, puede hacerlo con la ayuda de AlamofireImage. mis muestras son de su Github

Ejemplo de imágenes de marcador de posición:

let imageView = UIImageView(frame: frame)
let url = URL(string: "https://httpbin.org/image/png")!
let placeholderImage = UIImage(named: "placeholder")!
imageView.af_setImage(withURL: url, placeholderImage: placeholderImage)

Tiene muchas funciones útiles y extensión para trabajar con imágenes. desde el almacenamiento en caché hasta escalar y cambiar el tamaño o incluso aplicar filtros en la imagen. Si las imágenes son importantes en su aplicación, le sugiero que use este marco y ahorre tiempo.

Hamid Shahsavari
fuente
0

El uso de Ascyimageview puede cargar fácilmente imageurl en imageview.

deje que image1Url: URL = URL (string: "(imageurl)" como String)! imageview.imageURL = image1Url

saurabh rathod
fuente
0

Carga de imagen desde el servidor: -

func downloadImage(from url: URL , success:@escaping((_ image:UIImage)->()),failure:@escaping ((_ msg:String)->())){
    print("Download Started")
    getData(from: url) { data, response, error in
        guard let data = data, error == nil else {
            failure("Image cant download from G+ or fb server")
            return
        }

        print(response?.suggestedFilename ?? url.lastPathComponent)
        print("Download Finished")
        DispatchQueue.main.async() {
             if let _img = UIImage(data: data){
                  success(_img)
            }
        }
    }
}
func getData(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ()) {
    URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}

Uso: -

  if let url = URL(string: "http://www.apple.com/euro/ios/ios8/a/generic/images/og.png") {
                        self.downloadImage(from:url , success: { (image) in
                            print(image)

                        }, failure: { (failureReason) in
                            print(failureReason)
                        })
                    }
Abhimanyu Rathore
fuente
0

Para un mejor rendimiento UITableViewo UICollectionViewuso de la biblioteca de peso ligero Carga diferida inteligente Puede usar este enfoque de carga diferida si desea cargar imágenes desde la URL Asíncrono

Smart 'Lazy Loading' en UICollectionViewo UITableViewusando NSOperationy NSOperationQueueen iOS Por lo tanto, en este proyecto podemos descargar las imágenes múltiples en cualquier Vista ( UICollectionViewo UITableView) optimizando el rendimiento de una aplicación usando Operationy OperationQueuepara concurrencia. Los siguientes son los puntos clave de este proyecto Smart Lazy Loading: Creación del servicio de descarga de imágenes. Priorice la descarga en función de la visibilidad de las celdas.

La clase ImageDownloadService creará una instancia singleton y tendrá una instancia NSCache para almacenar en caché las imágenes que se han descargado. Hemos heredado la clase Operation a TOperation para modificar la funcionalidad de acuerdo con nuestras necesidades. Creo que las propiedades de la subclase de operación son bastante claras en términos de funcionalidad. Estamos monitoreando los cambios de estado de las operaciones mediante el uso de KVO.

jawadAli
fuente