I have the following code for my tableView
:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
// Reset the image in the cell
cell.coverImageView.image = nil
// Get the recipe that the tableView is asking about
let recipeInTable = recipe[indexPath.row]
cell.displayRecipe(recipe: recipeInTable, indexPathRow: indexPath.row)
return cell
}
This is my custom cell class, where I am caching the image data to pull from:
class CustomCell: UITableViewCell {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var coverImageView: UIImageView!
var recipeToDisplay:Recipe?
var recipeToPullFrom:Recipe!
func displayRecipe(recipe:Recipe, indexPathRow:Int) {
recipeToPullFrom = recipe
DispatchQueue.main.async {
self.titleLabel.text = self.recipeToPullFrom.title
if self.recipeToPullFrom.image == nil {
return
}
else {
let urlString = self.recipeToPullFrom.image
if let imageData = CacheManager.retrieveData(urlString!) {
self.coverImageView.image = UIImage(data: imageData)
return
}
let url = URL(string: urlString!)
guard url != nil else {
print("Could not create url object")
return
}
let session = URLSession.shared
let dataTask = session.dataTask(with: url!) { (data, response, error) in
if error == nil && data != nil {
CacheManager.saveData(urlString!, data!)
if self.recipeToPullFrom.image == urlString {
DispatchQueue.main.async {
// Display the image data in the imageView
self.coverImageView.image = UIImage(data: data!)
}
}
DispatchQueue.main.async {
self.coverImageView.image = UIImage(data: data!)
}
} // End if
} // End dataTask
// Kick off the dataTask
dataTask.resume()
}
}
}
}
And finally my Cache Manager:
class CacheManager {
static var imageDictionary = [String:Data]()
static func saveData(_ url:String, _ imageData:Data) {
// Save the image data along with the url
imageDictionary[url] = imageData
}
static func retrieveData(_ url:String) -> Data? {
// Return the saved imageData or nil
return imageDictionary[url]
}
}
From what I've researched, adding the following in my tableView
function should have reset the image in my cell before inputting a new one: cell.coverImageView.image = nil
.
Is there something I'm missing? I've also noticed that only my images are showing in the wrong cells. Could I be doing something incorrect with retrieving the image data from cache?
Any direction or support is much appreciated!
CodePudding user response:
Since your images are downloaded asynchronously then it can be downloaded after the cell been reused for another object. What you can do:
- When your image is ready to be displayed (downloaded) you need to check if it should be displayed in that cell.
Or
- You can also hold a reference to the
URLSessionDataTask
and cancel the it before reusing the cell by overriding theprepareForReuse
method.
CodePudding user response:
I don't think that you need here an asynchronously call especially, when you using .main
in other .main
. But If you what so you what so make some thing like
DispatchQueue.global(qos: .userInteractive).async {
// Never do here ui code - label, view, images.. anything; or would crash
DispatchQueue.main.async {
// To do here you ui updates
}
}
here is some refactored code with reuse method. I agree with Hach3m. U need to cancel requests if you don't it anymore, when next cell is about to shown
class CustomCell: UITableViewCell {
@IBOutlet private weak var titleLabel: UILabel!
@IBOutlet private weak var coverImageView: UIImageView!
private var dataTask: URLSessionDataTask?
func displayRecipe(recipe: Recipe, indexPathRow: Int) {
titleLabel.text = recipe.title enter code here
guard let urlString = recipe.image else { return }
if let imageData = CacheManager.retrieveData(urlString) {
coverImageView.image = UIImage(data: imageData)
return
}
guard let url = URL(string: urlString) else { return print("Could not create url object") }
dataTask = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return self.clear() }
CacheManager.saveData(urlString, data)
self.coverImageView.image = UIImage(data: data)
}
dataTask?.resume()
}
override func prepareForReuse() {
super.prepareForReuse()
clear()
}
private func clear() {
dataTask?.cancel()
dataTask = nil
}
}
}