I develop both android(Java) and IOS(swift) applications. I had a problem on the Android side and I solved it.I'm having the same problem on the IOS side but I couldn't solve it.
I have a tableview in my application and I fill it with data I get from my own server.
However, when I swipe through the list, sometimes the values and images in the list item get mixed up between the list items.
This little code on the Android side solved my problem
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemViewType(int position) {
return position;
}
I searched for override methods but I couldn't find an equivalent event that would solve my problem.
The list interface is like this, a simple list.
Maybe there is a problem with uploading the images, I upload it like this
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "mainCell", for: indexPath) as! MainTableCell
let item = radio_type_list[indexPath.row]
cell.RadioTitle.text = item.title
cell.RadioDescription.text = item.desc
cell.RadioImage.downloadImageFrom(link:item.imageurl, contentMode: UIView.ContentMode.scaleToFill)
return cell;
}
let imageCache = NSCache<NSString, AnyObject>()
extension UIImageView {
func downloadImageFrom(link:String, contentMode: UIView.ContentMode) {
let imageCache = NSCache<NSString, AnyObject>()
if let cachedImage = imageCache.object(forKey: (link as AnyObject) as! NSString) {
self.image = cachedImage as? UIImage
self.contentMode = .scaleAspectFill
return
}
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()
}
}
You can learn more about dequeue mechanisim
of ios following this link
So when you scroll through your list, whenever a cell get dequeue, you start a download image task
from an UIImageView
. The problem arise when cell got reuse, there is a chance the old download task
take longer time than the new download task
one for that cell. Here is my suggestion code to fix your problem:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "mainCell", for: indexPath) as! MainTableCell
let item = radio_type_list[indexPath.row]
cell.RadioTitle.text = item.title
cell.RadioDescription.text = item.desc
cell.bind(imageUrl: item.imageurl, contentMode: UIView.ContentMode.scaleToFill)
return cell;
}
class MainTableCell: UITableViewCell {
var RadioImage = UIImageView()
var imageUrl: String = ""
override func prepareForReuse() {
super.prepareForReuse()
RadioImage.image = nil // remove old image
}
func bind(imageUrl: String, contentMode: UIView.ContentMode) {
self.imageUrl = imageUrl
RadioImage.downloadImageFrom(link: imageUrl, contentMode: contentMode) { [weak self] _image, contentMode, imageUrl in
guard let self = self,
self.imageUrl == imageUrl // <- compare to current imageUrl of cell to display image correctly
else { return }
self.RadioImage.image = _image
self.RadioImage.contentMode = contentMode
}
}
}
let imageCache = NSCache<NSString, UIImage>()
extension UIImageView {
func downloadImageFrom(link:String, contentMode: UIView.ContentMode, completion: ((UIImage, UIView.ContentMode, String) -> Void)?) {
if let cachedImage = imageCache.object(forKey: (link as AnyObject) as! NSString) {
completion?(cachedImage, .scaleAspectFill, link)
return
}
URLSession.shared.dataTask( with: NSURL(string:link)! as URL, completionHandler: {
(data, response, error) -> Void in
DispatchQueue.main.async {
if let data = data,
let image = UIImage(data: data)
{
imageCache.setObject(image, forKey: link as NSString)
completion?(image, contentMode, link)
}
}
}).resume()
}
}