I want to use lazy loading concept for my table view using swift. In my table view i am showing multiple cells which contain product images and product name . Please help me to get the solution.
Asked
Active
Viewed 5.0k times
24
-
Duplicates a lot of questions. For instance http://stackoverflow.com/questions/16663618/async-image-loading-from-url-inside-a-uitableview-cell-image-changes-to-wrong – kean Nov 11 '15 at 13:33
-
1that one is with objc this is for swift, so still useful – mikey Aug 14 '19 at 06:45
3 Answers
42
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:

Community
- 1
- 1

Dharmesh Kheni
- 71,228
- 33
- 160
- 165
-
15This is good attempt, but has several flaws: 1. If cell was reused by the time the request finished, you could update the wrong row. 2. If user rapidly scrolls through the tableview, requests for visible cells can get back logged behind requests for cells that are no longer visible. We can remedy these issues, but I might instead suggest using `UIImageView` categories/extensions provided by SDWebImage or AFNetworking. – Rob May 22 '15 at 06:01
-
1
-
5
-
-
1It's better, and fine in trivial situations. But if on slow network or in situation where an image view might be reused (e.g. table views), it's problematic. It doesn't contemplate the cancelation of previous request if the image view can be reused (which means that visible cell requests will get backlogged behind requests for rows that are no longer visible and you'll see a flickering of images as various asynchronous image requests finish). It also doesn't do caching (other than what is provided automatically by `NSURLSession`). – Rob Sep 24 '15 at 18:56
-
2I'd still consider one of the richer `UIImageView` extensions, such as https://github.com/Alamofire/AlamofireImage or https://github.com/onevcat/Kingfisher or https://github.com/rs/SDWebImage or https://github.com/kean/DFImageManager – Rob Sep 24 '15 at 18:56
17
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)

Brad Root
- 483
- 5
- 14
-
-
Unable to download image...getting error : TIC SSL Trust Error, NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813) and Task <3E2...70>. HTTP load failed (error code: -1202). Any solution? – Jayprakash Dubey Jan 30 '18 at 04:33
-
@JayprakashDubey Are you trying to load from a HTTPS resource? There are some restrictions in iOS now that require HTTPS connections. – Brad Root Feb 15 '18 at 15:54
-
@BradRoot : Got this resolved by connecting to mobile data rather than office WiFi. Office WiFi has security restrictions. – Jayprakash Dubey Feb 16 '18 at 10:09
5
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 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
}
}
Result

Vasily Bodnarchuk
- 24,482
- 9
- 132
- 127