how to implement lazy loading of images in table view using swift
Old Solution:
Since you doesn't show any code.
Here is the example for you.
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}
Follow THIS tutorial for more Info. Hope this will help you.
New Solution:
Here is extension for it which is created by my friend Leo Dabus which is really simple to use:
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() }}
Now in your cellForRowAtIndexPath
method assign image to cell this way:
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.
And as Rob suggested into comment here is some useful libraries which you can use:
Since I can't comment just yet, here's a Swift 3 (Xcode 8 Beta 6) version of the useful extension provided by 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() }}
I'm using this inside a class that populates the table cell, it works like this in that context just fine, just in case any newbs were wondering if it will:
albumArt.image = UIImage(named: "placeholder")albumArt.downloadImageFrom(link: "http://someurl.com/image.jpg", contentMode: UIViewContentMode.scaleAspectFit)
Details
- Xcode 10.2.1 (10E1001), Swift 5
Full sample
Info.plist (add value)
<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 UIKitimport Alamofireimport AlamofireImageclass 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 }}