web-dev-qa-db-de.com

Schnelles asynchrones Ladebild

Ich arbeite an einem Show-Image von URL async. Ich habe versucht, einen neuen Thread für das Download-Image zu erstellen und dann auf main thread zu aktualisieren.

func asyncLoadImg(product:Product,imageView:UIImageView){
    let downloadQueue = dispatch_queue_create("com.myApp.processdownload", nil)
    dispatch_async(downloadQueue){
        let data = NSData(contentsOfURL: NSURL(string: product.productImage)!)
        var image:UIImage?
        if data != nil{
            image = UIImage(data: data!)
        }
        dispatch_async(dispatch_get_main_queue()){
            imageView.image = image
        }

    }
}

Wenn ich versuchte, das zu debuggen, wenn es zu dispatch_async (downloadQueue) kommt, springt es aus der Funktion heraus. Irgendein Vorschlag? Vielen Dank

25
Janice Zhan

Swift 3.0+ aktualisierter Code :( Hilfe von jamesrochabrun Link https://github.com/jamesrochabrun/JSONTutorialFinal )

let imageCache = NSCache<NSString, UIImage>()

extension UIImageView {

    func imageFromServerURL(_ 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: \(error)")
                    DispatchQueue.main.async {
                        self.image = placeHolder
                    }
                    return
                }
                DispatchQueue.main.async {
                    if let data = data {
                        if let downloadedImage = UIImage(data: data) {
                            imageCache.setObject(downloadedImage, forKey: NSString(string: URLString))
                            self.image = downloadedImage
                        }
                    }
                }
            }).resume()
        }
    }
}

Swift 3 Aktualisierter Code: 

extension UIImageView {
    public func imageFromServerURL(urlString: String) {
        self.image = nil
        URLSession.shared.dataTask(with: NSURL(string: urlString)! as URL, completionHandler: { (data, response, error) -> Void in

            if error != nil {
                print(error)
                return
            }
            DispatchQueue.main.async(execute: { () -> Void in
                let image = UIImage(data: data!)
                self.image = image
            })

        }).resume()
    }}

Swift 2.2 Code: 

 extension UIImageView {
        public func imageFromServerURL(urlString: String) {
    self.image = nil
            NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: urlString)!, completionHandler: { (data, response, error) -> Void in

                if error != nil {
                    print(error)
                    return
                }
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    let image = UIImage(data: data!)
                    self.image = image
                })

            }).resume()
        }}

Nun, wo immer Sie es gerade benötigen, um das Image von der Server-URL zu laden:

  1. Verwenden von Swift 3.0 + aktualisierter Code unter Verwendung des Platzhalterabbilds: UIImageView.imageFromServerURL (URLString: "Hier Server-URL", Platzhalter: Platzhalterbild im Uiimage-Format)

  2. Swfit 3 oder weniger: UIImageView.imageFromServerURL ("hier Server-URL") 

Einfach!

84

Verwenden Sie extension in Swift3. Zur Lösung des Netzwerkproblems empfehle ich die Verwendung von NSCache:

import UIKit

let imageCache = NSCache<NSString, AnyObject>()

extension UIImageView {
    func loadImageUsingCache(withUrl urlString : String) {
        let url = URL(string: urlString)
        self.image = nil

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

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

        }).resume()
    }
}

Ich hoffe es hilft!

22
javimuu

In Anlehnung an die Antwort von Shobhakar Tiwari halte ich es für hilfreich, in diesen Fällen im Fehlerfall ein Standardbild zu haben, und zu Ladezwecken. Daher habe ich es mit einem optionalen Standardbild aktualisiert:

Swift 3

extension UIImageView {
public func imageFromServerURL(urlString: String, defaultImage : String?) {
    if let di = defaultImage {
        self.image = UIImage(named: di)
    }

    URLSession.shared.dataTask(with: NSURL(string: urlString)! as URL, completionHandler: { (data, response, error) -> Void in

        if error != nil {
            print(error ?? "error")
            return
        }
        DispatchQueue.main.async(execute: { () -> Void in
            let image = UIImage(data: data!)
            self.image = image
        })

    }).resume()
  }
}
2
spogebob92

Diese Lösung ermöglicht ein schnelles Scrollen ohne unnötige Bildaktualisierungen. Sie müssen die URL-Eigenschaft unserer Zellklasse hinzufügen:

class OfferItemCell: UITableViewCell {
    @IBOutlet weak var itemImageView: UIImageView!
    @IBOutlet weak var titleLabel: UILabel! 
    var imageUrl: String?
}

Und fügen Sie die Erweiterung hinzu:

import Foundation
import UIKit

let imageCache = NSCache<AnyObject, AnyObject>()
let imageDownloadUtil: ImageDownloadUtil = ImageDownloadUtil()

extension OfferItemCell {
    func loadImageUsingCacheWithUrl(urlString: String  ) {
         self.itemImageView.image = nil
        if let cachedImage = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
            self.itemImageView.image = cachedImage
            return
        }
        DispatchQueue.global(qos: .background).async {
            imageDownloadUtil.getImage(url: urlString, completion: {
                image in
                DispatchQueue.main.async {
                    if self.imageUrl == urlString{
                        imageCache.setObject(image, forKey: urlString as AnyObject)
                     self.itemImageView.image = image
                    }
                }
            })
        }
    }
}

Sie können ihn auch verbessern und etwas Code in eine allgemeinere Zellklasse extrahieren, d. H. CustomCellWithImage, um ihn wiederverwendbar zu machen.

1

Die üblichste Methode, in Swift 4 zum Laden asynchroner Bilder ohne blink- oder changing images-Effekt zu verwenden, ist die Verwendung einer benutzerdefinierten UIImageView-Klasse wie dieser:

//MARK: - 'asyncImagesCashArray' is a global varible cashed UIImage
var asyncImagesCashArray = NSCache<NSString, UIImage>()

class AyncImageView: UIImageView {

//MARK: - Variables
private var currentURL: NSString?

//MARK: - Public Methods

func loadAsyncFrom(url: String, placeholder: UIImage?) {
    let imageURL = url as NSString
    if let cashedImage = asyncImagesCashArray.object(forKey: imageURL) {
        image = cashedImage
        return
    }
    image = placeholder
    currentURL = imageURL
    guard let requestURL = URL(string: url) else { image = placeholder; return }
    URLSession.shared.dataTask(with: requestURL) { (data, response, error) in
        DispatchQueue.main.async { [weak self] in
            if error == nil {
                if let imageData = data {
                    if self?.currentURL == imageURL {
                        if let imageToPresent = UIImage(data: imageData) {
                            asyncImagesCashArray.setObject(imageToPresent, forKey: imageURL)
                            self?.image = imageToPresent
                        } else {
                            self?.image = placeholder
                        }
                    }
                } else {
                    self?.image = placeholder
                }
            } else {
                self?.image = placeholder
            }
        }
    }.resume()
}
}

beispiel für die Verwendung dieser Klasse in UITableViewCell unten:

class CatCell: UITableViewCell {

//MARK: - Outlets
@IBOutlet weak var catImageView: AyncImageView!

//MARK: - Variables

var urlString: String? {
    didSet {
        if let url = urlString {
            catImageView.loadAsyncFrom(url: url, placeholder: nil)
        }
    }
}

override func awakeFromNib() {
    super.awakeFromNib()
}
}
1
PiterPan

Hier könnte Ihnen dieser Code helfen.

   let cacheKey = indexPath.row
   if(self.imageCache?.objectForKey(cacheKey) != nil){
           cell.img.image = self.imageCache?.objectForKey(cacheKey) as? UIImage
    }else{
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), {
             if let url = NSURL(string: imgUrl) {
                  if let data = NSData(contentsOfURL: url) {
                            let image: UIImage = UIImage(data: data)!
                            self.imageCache?.setObject(image, forKey: cacheKey)
                            dispatch_async(dispatch_get_main_queue(), {
                                cell.img.image = image
                            })
                        }
                    }
                })
            }

Mit diesem Bild wird heruntergeladen und zwischengespeichert, ohne die Bildlaufleiste der Tabelle zu verzögern 

1

Eine der besten Möglichkeiten ist die Verwendung von SDWebImage

Schnelles Beispiel: 

import SDWebImage
imageView.sd_setImage(with: URL(string: "ImageUrl"), placeholderImage: UIImage(named: "placeholder.png"))

Ziel C-Beispiel: 

#import <SDWebImage/UIImageView+WebCache.h>
[imageView sd_setImageWithURL:[NSURL URLWithString:@"ImageUrl"]
         placeholderImage:[UIImage imageNamed:@"placeholder.png"]];
0
Rajesh