Chargement / téléchargement d'image à partir d'une URL sur Swift


415

Je voudrais charger une image à partir d'une URL dans mon application, j'ai donc d'abord essayé avec Objective-C et cela a fonctionné, cependant, avec Swift, j'ai une erreur de compilation:

'imageWithData' n'est pas disponible: utilisez la construction d'objet 'UIImage (data :)'

Ma fonction:

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

Dans l'objectif-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";
 }

Quelqu'un peut-il m'expliquer pourquoi imageWithData:cela ne fonctionne pas avec Swift et comment puis-je résoudre le problème?


3
Essayez ceciimageURL.image = UIImage(data: myDataVar)
aleclarson

Parfait, cela a fonctionné! Merci Cependant je ne sais pas pourquoi cette méthode fonctionne en Objective C, et pas en Swift ... Strange
QuentR

Lorsque vous rencontrez des problèmes avec une classe Cocoa, essayez CMD + en cliquant sur le nom de la classe et vous devriez pouvoir voir l'interface Swift de la classe!
aleclarson

3
if let url = NSURL (string: "imageurl") {if let data = NSData (contentsOfURL: url) {imageView.image = UIImage (data: data)}}
Hardik Bar

2
@Leo Dabus Cette question n'est pas spécifique à Swift 2. Veuillez arrêter d'y ajouter cette balise.
Mick MacCallum

Réponses:


771

Xcode 8 ou version ultérieure • Swift 3 ou version ultérieure

Synchrone:

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

De manière asynchrone:

Créez une méthode avec un gestionnaire d'achèvement pour obtenir les données d'image de votre URL

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

Créer une méthode pour télécharger l'image (démarrer la tâche)

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

Usage:

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

Extension :

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

Usage:

imageView.downloaded(from: "https://cdn.arstechnica.net/wp-content/uploads/2018/06/macOS-Mojave-Dynamic-Wallpaper-transition.jpg")

13
Juste une note latérale ici, vous devez lui attribuer un objet associé; Sinon, vous risquez de charger des images les unes sur les autres. Par exemple, dans un UITableViewendroit où une cellule affiche une image et l'image est mise à jour lorsque la cellule retirée de la file d'attente est renvoyée. Si le processus n ° 1 prend plus de temps que le processus n ° 2, le processus n ° 2 affichera son image et il sera ensuite mis à jour par le processus n ° 1 même si cette image n'est plus valide pour l'utilisateur.
Paul Peelen

1
Cela m'a résolu mon problème. Lorsque j'essaie de charger l'image avec l'URL en mode tableau, la vue de défilement ne répond vraiment pas lors du défilement vers le haut ou vers le bas. J'ai utilisé l'extension UIImageView et cela a résolu mon problème. Merci.
JengGe Chao

1
@LeoDabus ok merci. et merci d'avoir répondu à tant de questions sur SO! vous êtes comme un professeur de swift et d'iOS. :)
Crashalot

2
@LeoDabus comment se fait-il que vous ayez utilisé dataTaskWithURL au lieu de downloadTaskWithURL?
Crashalot

3
Le premier télécharge en mémoire le second dans un fichier
Leo Dabus

350

(Mise à jour Swift 4) Pour répondre directement à la question d'origine, voici l'équivalent rapide de l'extrait Objective-C publié.

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

AVERTISSEMENT:

Il est important de noter que la Data(contentsOf:)méthode télécharge le contenu de l'URL de manière synchrone dans le même thread que le code en cours d'exécution, donc ne l' invoquez pas dans le thread principal de votre application.

Un moyen simple de faire fonctionner le même code de manière asynchrone, sans bloquer l'interface utilisateur, est d'utiliser 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!)
    }
}

Cela dit, dans les applications réelles, si vous voulez avoir la meilleure expérience utilisateur et éviter plusieurs téléchargements de la même image, vous pouvez également les avoir téléchargés, mais mis en cache. Il y a déjà pas mal de bibliothèques qui font ça de façon très transparente et elles sont toutes vraiment faciles à utiliser. Je recommande personnellement 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) 

Et c'est tout


12
"Facilement" est subjectif, et les codeurs débutants ne s'attendront pas à ce comportement indésirable, mais copiez / collez celui-ci tel quel. Peut-être pouvez-vous mettre à jour votre réponse?
Orlin Georgiev du

13
Je ne suis toujours pas d'accord. Certaines réponses doivent rester simples, beaucoup de gens qui viennent ici aiment copier et coller de petits extraits de code. Je viens de traduire à Swift le code objectif-C de la question d'origine, tout le reste est un bonus. Et en passant, il y a déjà une réponse à cette même question en fournissant ce type d'informations bonus, ce qui a moins de sens de dupliquer des informations.
Lucas Eduardo

1
@User remplace simplement le image.urlpar n'importe quelle URL de chaîne, codée en dur ou non :)
Lucas Eduardo

1
@IanWarburton Bien sûr, vous pouvez utiliser ce que vous voulez :). 1) Cette réponse est basée sur la question d'origine, qui utilisait la même approche dans l'objectif-C, donc j'ai juste aidé à la «traduction» pour accélérer. 2) Il n'est pas logique de supprimer cette approche et de la mettre URLSession.dataTaskcar beaucoup d'autres réponses ici montrent déjà comment le faire, il est préférable de garder les différentes options ouvertes.
Lucas Eduardo

2
Heyo, le cadre suggéré, martin-pêcheur, comme indiqué dans la réponse, fera tout le travail lourd. Merci pour la suggestion!
Kilmazing

68

Si vous voulez juste charger l'image (de manière asynchrone!) - ajoutez simplement cette petite extension à votre code rapide:

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

Et utilisez-le de cette façon:

myImageView.imageFromUrl("https://robohash.org/123.png")

1
Il ne se présente pas, et je pense qu'il y a un besoin de GCD. Comment rafraîchir?
jo3birdtalk

@ jo3birdtalk ce n'est pas un problème de GCD. Vérifiez vos fixations avec vue.
skywinder

6
NSURLConnection est déconseillé sur iOS 9 et ainsi de suite. Utilisez NSURLSession intead.
muhasturk

2
sendAsynchronousRequest est déconseillé dans iOS 9
Crashalot

Merci skywinder, j'utilise cette méthode pour télécharger des images à partir d'un tableau. Je veux que lorsque l'utilisateur appuie sur le cancelbouton, le téléchargement s'arrête. Avez-vous une idée de comment je peux le faire en utilisant cette méthode? J'ai besoin d'ajouter une fonctionnalité d'annulation.
ZAFAR007

44

Swift 2.2 || Xcode 7.3

J'ai obtenu des résultats incroyables !! avec la bibliothèque rapide AlamofireImage

Il offre plusieurs fonctionnalités comme:

  • Téléchargement asynchrone
  • Purge automatique du cache d'images si des avertissements de mémoire se produisent pour l'application
  • Mise en cache de l'URL de l'image
  • Mise en cache d'image
  • Évitez les téléchargements en double

et très facile à mettre en œuvre pour votre application

Étape 1 Installer des modules


Alamofire 3.3.x

pod 'Alamofire'

AlamofireImage 2.4.x

pod 'AlamofireImage'

Étape 2: importation et utilisation

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)

c'est ça!! il prendra soin de tout


Un grand merci aux gars d'Alamofire , pour avoir rendu la vie iDevelopers facile;)


8
Swift 3: foregroundImage.af_setImage (withURL: downloadURL as URL)
thejuki

28

Xcode 8Swift 3

La réponse de Leo Dabus est impressionnante! Je voulais juste fournir une solution de fonction tout-en-un:

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

3
Mark, signifiait probablement "async" à la fin là
Fattie

16

J'ai encapsulé le code des meilleures réponses à la question dans une seule classe réutilisable étendant UIImageView, afin que vous puissiez utiliser directement le chargement asynchrone UIImageViews dans votre storyboard (ou les créer à partir du code).

Voici ma classe:

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

et voici comment l'utiliser:

imageView.downloadImage("http://www.image-server.com/myImage.jpg")

2
Avons-nous vraiment besoin de ces inits remplacés? Les inits ne sont-ils pas hérités de toute façon? On dirait que nous ne faisons que passer outre ici juste pour appeler super sur lui-même, ce qui me semble redondant.
NYC Tech Engineer

16

Swift 4 ::

Cela montrera le chargeur lors du chargement de l'image. Vous pouvez utiliser NSCache qui stocke temporairement l'image

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

Usage:-

truckImageView.loadImageUsingCache(withUrl: currentTruck.logoString)

1
Ça marche. Copiez simplement la suppression de activityIndicator dans le cas d'erreur également.
djdance

1
très utile pour mon projet.
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)

5
fatal error: unexpectedly found nil while unwrapping an Optional value
Utilisateur

Avez-vous des chances d'écrire quelque chose d'asynchrone?
Jed Grant

@JedGrant, vous pouvez utiliser dispatch_async pour entrer dans un autre thread et un rappel
Matej

J'ai dû déballer le NSData avec un "!" (notez le! à la fin) pour le faire fonctionner comme ceci: var imageData: NSData = NSData (contentsOfURL: url, options: NSDataReadingOptions.DataReadingMappedIfSafe, erreur: & err)!
botbot

13

Pour info: pour 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!)
            }
        }
    }
}

5
NSURLConnection est obsolète. Vous devez utiliser NSURLSession. C'est mieux lorsque vous
codez

11

swift 3 avec gestion des erreurs

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

Avec extension

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

Utilisation de l'extension

myImageView. setCustomImage("url")

Avec prise en charge du cache

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

9

Swift 4: Un simple chargeur pour les petites images (ex: vignettes) qui utilise NSCache et s'exécute toujours sur le thread 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)) }
    }
  }

}

Usage:

ImageLoader.image(for: imageURL) { image in
  self.imageView.image = image
}

Pourquoi utiliser singleton avec struct? cela rendra votre classe immuable, ce qui peut provoquer des problèmes avec votre NSCache
XcodeNOOB

Merci de l'avoir signalé, je viens de le remplacer par class.
fethica

1
le cache de let privé me donnait une erreur alors je l'ai changé en statique et cela a fonctionné! Merci
Hammad Tariq

7

Vous aurez envie de faire:

UIImage(data: data)

Dans Swift, ils ont remplacé la plupart des méthodes d'usine Objective C par des constructeurs réguliers.

Voir:

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


2
Mappé techniquement, non remplacé. Si vous créez votre propre méthode +(instancetype)[MyThing thingWithOtherThing:], vous l'appellerez aussi comme MyThing(otherThing: ...)dans Swift.
Brian Nickel

7

Swift 2 avec poignée d'erreur et en-tête de demande personnalisé

Ajoutez simplement l'extension à 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()
        }
    }
}

Et puis, utilisez le nouveau imageFromUrl(urlString: String)pour télécharger l'image

Usage:

imageView.imageFromUrl("https://i.imgur.com/ONaprQV.png")

dans swift 3, je reçois toujours cette erreur. Quel est le problème ? Dans cette ligne "" "URLSession.shared.dataTask (avec: url) {" "" "" "" Impossible d'appeler 'dataTask' avec une liste d'arguments de type '(avec: URL, (Data ?, URLResponse ?, Error? ) -> Void) '
Aymen BRomdhane

7

Swift 4

Cette méthode téléchargera une image d'un site Web de manière asynchrone et la mettra en cache:

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

Utilisé:

    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
    }

Comment c'est la mise en cache? Et pour combien de temps?
Ramis

Il semble être stocké en permanence, ce qui est bizarre, il est stocké dans le dossier Library> Caches. Utilisez print (NSHomeDirectory ()) pour accéder à cet emplacement sur votre ordinateur lorsqu'il est exécuté sur le simulateur.
Bobby

5

Swift 2.0:

1)

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

OU

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

2) Ajoutez cette méthode au VC ou à l'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)
        }
    }
}

Utilisation:

self.load_image(" url strig here")

sendAsynchronousRequest est déconseillé dans iOS 9
Crashalot

5

Kingfisher est l'une des meilleures bibliothèques pour charger l'image dans l'URL.

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

5

Voici le code de travail pour charger / télécharger l'image à partir de l'URL. NSCache automatiquement et afficher l'image d'espace réservé avant le téléchargement et charger l'image réelle (code 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!
                        }
                    }
                }
            }
        }
    }
}

Utilisez comme ceci:

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

1
Le seul qui a fonctionné avec moi. mon problème était que j'implémentais un lecteur de musique et je voulais que l'image se charge dans la notification lorsque le téléphone est également fermé et que le gestionnaire prend le type d'UIImage, j'ai donc dû utiliser cette méthode.
JhonnyTawk

4

Une méthode pour obtenir l'image qui est sûre et fonctionne avec Swift 2.0 et 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)
}

Vous appelleriez alors cette méthode comme ceci:

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 vous mettez à jour la vue avec l'image, vous devrez l'utiliser après le "si succès {":

    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 raison pour laquelle cette dernière partie est nécessaire si vous utilisez l'image dans l'interface utilisateur est que les appels réseau prennent du temps. Si vous essayez de mettre à jour l'interface utilisateur à l'aide de l'image sans appeler dispatch_async comme ci-dessus, l'ordinateur recherchera l'image pendant que l'image est encore en cours de recherche, trouvera qu'il n'y a pas (encore) d'image et continuera comme s'il n'y avait pas d'image a trouvé. Mettre votre code à l'intérieur d'une fermeture d'achèvement de dispatch_async dit à l'ordinateur: "Allez chercher cette image et lorsque vous avez terminé, puis complétez ce code." De cette façon, vous aurez l'image lorsque le code sera appelé et les choses fonctionneront bien.


4

Si vous recherchez une implémentation très très simple. (Cela a fonctionné pour moi dans 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)

J'ai implémenté une vue de table avec une cellule personnalisée qui n'a qu'une image

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

    }

vous avez raison, c'est parce que le téléchargement et la compression se produisent sur le thread principal. vous devriez idéalement faire ces 2 étapes de manière asynchrone dans un thread d'arrière-plan, utilisez peut-être la file d'attente globale dispatch_async
Naishta

Oui, en utilisant le thread d'arrière-plan, nous pouvons optimiser la vitesse de téléchargement et si nécessaire, nous pouvons changer la logique au lieu de cela, nous pouvons utiliser sdWebImage ou un autre cadre.
Gourav Joshi

2

Je recommande d'utiliser la bibliothèque Kingfisher pour télécharger des images de manière asynchrone. La meilleure partie de l'utilisation de Kingfisher est qu'il met en cache toutes les images téléchargées par défaut avec l'URL de l'image comme identifiant. La prochaine fois que vous demanderez de télécharger l'image avec cette URL particulière, il la chargera depuis le cache.

Usage:

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

2

Vous pouvez utiliser le pod SDWebImagepour obtenir le même résultat. C'est facile à utiliser. Vous pouvez obtenir de la documentation ici SDWebImage

Voici l exemple de code

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

2

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

pour l'utilisation

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

1

La seule chose qui manque est 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!)

Désolé pour la réponse tardive, pourriez-vous être plus précis sur les erreurs que vous obtenez?
Simon Jensen

1

Réponse Swift 2.x qui télécharge l'image dans un fichier (contrairement à la réponse de Leo Dabus, qui stocke l'image en mémoire). Basé sur la réponse de Leo Dabus et la réponse de Rob de Get the data from NSURLSession DownloadTaskWithRequest du gestionnaire d'achèvement :

    // 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

}

1

Swift 4.1 J'ai créé une fonction, il suffit de passer l'URL de l'image, la clé de cache après la génération de l'image, la définir sur le bloc d'achèvement.

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

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

J'ai créé une fonction de classe simple dans ma Utils.swiftclasse pour appeler cette méthode à laquelle vous pouvez simplement accéder classname.methodnameet vos images sont enregistrées dans NSCache en utilisant la ImageCaching.swiftclasse

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

Codding heureux. À votre santé:)


1

Swift 4.2 et AlamofireImage

Si l'utilisation d'une bibliothèque n'est pas un problème, vous pouvez le faire à l'aide du AlamofireImage. mes échantillons proviennent de son Github

Exemple d'images d'espace réservé:

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)

il a de nombreuses fonctions pratiques et extension pour travailler avec des images. de la mise en cache à la mise à l'échelle et au redimensionnement ou même à l'application de filtres sur l'image. si les images sont importantes dans votre application, je vous suggère d'utiliser ce framework et de gagner du temps.


0

L'utilisation d'Ascyimageview vous permet de charger facilement imageurl dans imageview.

laissez image1Url: URL = URL (chaîne: "(imageurl)" comme chaîne)! imageview.imageURL = image1Url


0

Chargement d'image depuis le serveur: -

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

Utilisation: -

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

0

Pour de meilleures performances dans UITableViewouUICollectionView utilisez une bibliothèque légère Chargement intelligent paresseux Vous pouvez utiliser cette approche de chargement paresseux si vous souhaitez charger des images à partir de l'url Asynchrone

Smart 'Lazy Loading' dans UICollectionViewou en UITableViewutilisant NSOperationet NSOperationQueuedans iOS Donc, dans ce projet, nous pouvons télécharger les multiples images dans n'importe quelle vue ( UICollectionViewou UITableView) en optimisant les performances d'une application en utilisantOperation et OperationQueuepour la concurrence. Voici le point clé de ce projet Smart Lazy Loading: Création d'un service de téléchargement d'images. Priorisez le téléchargement en fonction de la visibilité des cellules.

La classe ImageDownloadService créera une instance singleton et aura une instance NSCache pour mettre en cache les images qui ont été téléchargées. Nous avons hérité de la classe Operation de TOperation pour réduire la fonctionnalité en fonction de nos besoins. Je pense que les propriétés de la sous-classe d'opération sont assez claires en termes de fonctionnalité. Nous suivons les opérations de changement d'état en utilisant KVO.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.