web-dev-qa-db-fra.com

comment implémenter le chargement paresseux d'images en mode tableau en utilisant swift

Je veux utiliser le concept de chargement paresseux pour ma vue de table à l'aide de Swift. Dans ma vue tableau, je montre plusieurs cellules qui contiennent des images de produit et le nom du produit. S'il vous plaît, aidez-moi à trouver la solution.

17
Vikram Pote

Ancienne solution:

Puisque vous n'affichez aucun code.

Voici l'exemple pour vous.

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

    // try to reuse cell
    let cell:CustomCell = tableView.dequeueReusableCellWithIdentifier("DealCell") as CustomCell

    // get the deal image
    let currentImage = deals[indexPath.row].imageID
    let unwrappedImage = currentImage
    var image = self.imageCache[unwrappedImage]
    let imageUrl = NSURL(string: "http://staging.api.cheapeat.com.au/deals/\(unwrappedImage)/photo")

    // reset reused cell image to placeholder
    cell.dealImage.image = UIImage(named: "placeholder")

    // async image
    if image == nil {

    let request: NSURLRequest = NSURLRequest(URL: imageUrl!)

    NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!,data: NSData!,error: NSError!) -> Void in
        if error == nil {

            image = UIImage(data: data)

            self.imageCache[unwrappedImage] = image
            dispatch_async(dispatch_get_main_queue(), {
                cell.dealImage.image = image

            })
        }
        else {

        }
    })
    }

    else{
        cell.dealImage.image = image
    }

  return cell

}

Suivez CE tutoriel pour plus d'informations. J'espère que cela vous aidera.

Nouvelle solution:

Voici une extension créée par mon ami Leo Dabus qui est vraiment simple à utiliser:

extension UIImageView {
    func downloadImageFrom(link link:String, contentMode: UIViewContentMode) {
        NSURLSession.sharedSession().dataTaskWithURL( NSURL(string:link)!, completionHandler: {
            (data, response, error) -> Void in
            dispatch_async(dispatch_get_main_queue()) {
                self.contentMode =  contentMode
                if let data = data { self.image = UIImage(data: data) }
            }
        }).resume()
    }
}

Maintenant, dans votre méthode cellForRowAtIndexPath, affectez l'image à la cellule de cette façon:

cell.cellImageView.image = UIImage(named: "placeholder")  //set placeholder image first.
cell.cellImageView.downloadImageFrom(link: imageLinkArray[indexPath.row], contentMode: UIViewContentMode.ScaleAspectFit)  //set your image from link array.

Et comme Rob suggéré dans le commentaire, voici quelques bibliothèques utiles que vous pouvez utiliser:

  1. https://github.com/Alamofire/AlamofireImage
  2. https://github.com/onevcat/Kingfisher
  3. https://github.com/rs/SDWebImage
  4. https://github.com/kean/DFImageManager
36
Dharmesh

Comme je ne peux pas encore commenter, voici une version Swift (Xcode 8 Beta 6) de l'extension utile fournie par Leo Dabus.

extension UIImageView {
    func downloadImageFrom(link:String, contentMode: UIViewContentMode) {
        URLSession.shared.dataTask( with: NSURL(string:link)! as URL, completionHandler: {
            (data, response, error) -> Void in
            DispatchQueue.main.async {
                self.contentMode =  contentMode
                if let data = data { self.image = UIImage(data: data) }
            }
        }).resume()
    }
}

J'utilise ceci dans une classe qui remplit la cellule du tableau, cela fonctionne très bien comme ça dans ce contexte, juste au cas où des newbs se demanderaient si cela le ferait:

albumArt.image = UIImage(named: "placeholder")
albumArt.downloadImageFrom(link: "http://someurl.com/image.jpg", contentMode: UIViewContentMode.scaleAspectFit)
13
Brad Root

Détails

  • Xcode 10.2.1 (10E1001), Swift 5

Échantillon complet

Info.plist (ajouter de la valeur)

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

Podfile

target 'stackoverflow-28694645' do
  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!

  # Pods for stackoverflow-28694645
  pod 'Alamofire'
  pod 'AlamofireImage'
end

Code

import UIKit
import Alamofire
import AlamofireImage

class ViewController: UIViewController {

    private weak var tableView: UITableView?
    private var items = [ItunceItem]()

    override func viewDidLoad() {
        super.viewDidLoad()
        let tableView = UITableView()
        view.addSubview(tableView)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
        tableView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
        tableView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
        tableView.tableFooterView = UIView()
        tableView.register(TableViewCell.self, forCellReuseIdentifier: "TableViewCell")
        tableView.rowHeight = 100
        tableView.separatorColor = .clear
        self.tableView = tableView
        loadData()
    }

    private func loadData() {
        let urlString = "https://iTunes.Apple.com/search?term=navigator"
        Alamofire.request(urlString).response { [weak self] response in
            guard let self = self, let data = response.data else { return }
            do {
                let decoder = JSONDecoder()
                decoder.keyDecodingStrategy = .convertFromSnakeCase
                self.items = try decoder.decode(ItunceItems.self, from: data).results
                DispatchQueue.main.async { [weak self] in
                    guard let tableView = self?.tableView else { return }
                    tableView.delegate = self
                    tableView.dataSource = self
                    tableView.reloadData()
                }
            } catch let error { print("\(error.localizedDescription)") }
        }
    }
}

extension ViewController: UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int { return 1 }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return items.count }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
        return cell
    }
}

extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        guard   let cell = cell as? TableViewCell,
                let imageUrlString = items[indexPath.row].artworkUrl100,
                let url = URL(string: imageUrlString) else { return }
        cell.photoImageView?.af_setImage(withURL: url)
    }
    func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        guard let cell = cell as? TableViewCell else { return }
        cell.photoImageView?.af_cancelImageRequest()
    }
}

struct ItunceItems: Codable { let results: [ItunceItem] }
struct ItunceItem: Codable { var artworkUrl100: String? }

class TableViewCell: UITableViewCell {

    private(set) weak var photoImageView: UIImageView?
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        selectionStyle = .none
        let imageView = UIImageView()
        addSubview(imageView)
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 6).isActive = true
        imageView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor).isActive = true
        imageView.leftAnchor.constraint(equalTo: safeAreaLayoutGuide.leftAnchor).isActive = true
        imageView.rightAnchor.constraint(equalTo: safeAreaLayoutGuide.rightAnchor).isActive = true
        imageView.contentMode = .scaleAspectFit
        photoImageView = imageView
    }

    required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
    override func prepareForReuse() {
        super.prepareForReuse()
        photoImageView?.image = nil
    }
}

Résultat

enter image description here

5