I am having an issue, where I have a set of high resolution images which I display in a UITableView.
I am not doing something right when it comes to caching the images. I also tried and failed the similar implementation with AlamofireImage library and it seems like I find the scrolling is not that smooth in both of the implementations.
However, if I resize the images to a smaller resolution I don't see any problems. But I want to load images with their exact resolutions.
Can anyone tell me what might be the issue?
the code is as follows:
override func viewDidLoad() {
super.viewDidLoad()
for i in 1000...2000{
let url = "https://i.picsum.photos/id/\(i)/3000/2000.jpg";
imageUrlArray.append(url)
print(url)
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ImageCellId") as! ImageTableViewCell
let imageUrl = imageUrlArray[indexPath.row]
cell.imgView.image = nil
cell.tag = indexPath.row
if let image = imageCache.object(forKey: indexPath.row as AnyObject){
DispatchQueue.main.async {
cell.imgView?.image = image as? UIImage
}
}
DispatchQueue.main.async {
let data = NSData(contentsOf: URL(string: imageUrl)!)
guard let imageData = data else{
return
}
DispatchQueue.main.async {
cell.imgView.image = UIImage(data:imageData as Data)
imageCache.setObject(imageData, forKey: indexPath.row as AnyObject)
print("Setting object")
}
}
return cell
}
With Alamofire:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ImageCellId") as! ImageTableViewCell
let imageUrl = imageUrlArray[indexPath.row]
cell.imgView.image = nil
cell.tag = indexPath.row
Alamofire.request(imageUrl).responseData { response in
if case .success(let image) = response.result {
DispatchQueue.main.async {
if(cell.tag == indexPath.row){
guard let imageData = UIImage(data: image)else{
return
}
cell.imgView.image = imageData
}
}
}
}
return cell
}
Your first attempt with NSData(contentsOf:)
on the main thread is will definitely not work well. That is blocking the main thread to perform synchronous network requests. The Alamofire approach is more promising, pushing the requests into an asynchronous process, which should largely alleviate the problem.
That having been said, using assets that are larger than the control in which you will be using them can result in some stuttering in the UI as large assets are uncompressed and dynamically rescaled within the control. Regardless of the size of images that you’re fetching, you will want to resize them appropriate for your UI. This is easily done with AlamofireImage’s setImage(withURL:)
.
For example, in AlamofireImage 4:
let urls = (1000...2000).compactMap { URL(string: "https://i.picsum.photos/id/\($0)/3000/2000.jpg") }
let placeholder = UIImage()
let filter: AspectScaledToFillSizeFilter = {
let scale = UIScreen.main.scale
let size = CGSize(width: 50 * scale, height: 50 * scale)
return AspectScaledToFillSizeFilter(size: size)
}()
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
let url = urls[indexPath.row]
cell.customImageView.af.setImage(withURL: url, cacheKey: url.absoluteString, placeholderImage: placeholder, filter: filter, imageTransition: .crossDissolve(0.2), runImageTransitionIfCached: false)
return cell
}
This downloads the high resolution image, resizes it appropriately for the image view (in this case, my image view is 50×50 points), and does the in-memory caching.
If you want to prefetch the images, you can specify a prefetch data source:
override func viewDidLoad() {
super.viewDidLoad()
tableView.prefetchDataSource = self
}
And have it download images:
extension ViewController: UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
indexPaths.map { urls[$0.row] }.forEach { url in
let request = URLRequest(url: url)
let key = url.absoluteString
UIImageView.af.sharedImageDownloader.download(request, cacheKey: key, filter: filter, completion: nil)
}
}
}