Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
235 views
in Technique[技术] by (71.8m points)

ios - swift: load the image data in background

I need to load 100 images in cells. If I use this method in tableView cellForRowAt:

cell.cardImageView.image = UIImage(named: "(indexPath.row + 1).jpg")

and start scrolling fast my tableView freezes.

I use this method to load the image data in background that fix freezes:

func loadImageAsync(imageName: String, completion: @escaping (UIImage) -> ()) {
    DispatchQueue.global(qos: .userInteractive).async {
        guard let image = UIImage(named: imageName) else {return}
        DispatchQueue.main.async {
            completion(image)
        }
    }
}

in tableView cellForRowAt call this:

loadImageAsync(imageName: "(indexPath.row + 1).jpg") { (image) in
        cell.cardImageView.image = image
}

But I have one bug may arise in this approach, such that while scrolling fast I may see old images for a while. How to fix this bug?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Your cells are being reused.

When cell goes out of screen it gets to internal UITableViews reuse pool, and when you dequeueReusableCell(withIdentifier:for:) in tableView(_:cellForRowAt:) you get this cell again (see, "reusable" in name). It is important to understand UITableViewCell's life cycle. UITableView does not hold 100 living UITableViewCells for 100 rows, that would kill performance and leave apps without memory pretty soon.

Why do you see old images in your cells? Again, cells are being reused, they keep their old state after reuse, you'll need to reset the image, they won't reset it by themselves. You can do that when you configure a new cell or detect when the cell is about to be reused.

As simple as:

cell.cardImageView.image = nil // reset image
loadImageAsync(imageName: "(indexPath.row + 1).jpg") { (image) in
        cell.cardImageView.image = image
}

The other way is detecting reuse and resetting. In your cell subclass:

override func prepareForReuse() {
    super.prepareForReuse()
    self.cardImageView.image = nil // reset
}

Why do you see wrong images in your cells? By the time completion closure sets image into cardImageView, UITableViewCell has been reused (maybe, even, more than once). To prevent this you could test if you're setting image in the same cell, for example, store image name with your cell, and then:

// naive approach

let imageName = "(indexPath.row + 1).jpg"
cell.imageName = imageName
loadImageAsync(imageName: imageName) { (image) in
    guard cell.imageName == imageName else { return } 
    cell.cardImageView.image = image
}

There is a lot of stuff to take care of when designing lists, I won't be going into much detail here. I'd suggest to try the above approach and investigate the web on how to handle performance issues with lists.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...