8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

首次加载时来自 URL 的 UITableViewCell 图像错误

alexmherrmann 2月前

225 0

情况将 url 图像设置为 UITableViewCell 时出现问题。我创建了 CancelableImageView,它是自定义 UIImageView,用于在有新图像时取消图像下载和设置任务……

情况

将 url 图像设置为 UITableViewCell 时出现了问题。我创建了自定义 CancelableImageView UIImageView 当设置新图像时取消图像下载和设置任务。使用此方法,“错误图像”问题似乎已解决,但这仍然发生在 UITableView 上

加载 UITableView 第一个单元格 完成加载图像并更改图像后, 第四个单元格 的图像也更改为 第一个单元格的图像 。 滚动后,第四个单元格的图像更改为正确的图像。

代码

这是我的 UIImage 图像加载扩展(包含图像缓存)

/// Singleton for Image Caching
class ImageCacheManager {
    
    /// Storage to save cached UIImage
    private let cache = Cache<String, UIImage>()
    
    /// singleton instance
    static let shared = ImageCacheManager()
    
    /**
    Get image from cache data for url String
    - Parameter key:  String url of UIImage
    - Returns: Retrun cached image for url. Retrun nil when cached image is not exist
    */
    func loadCachedData(for key: String) -> UIImage? {
        let itemURL = NSString(string: key)
        return cache.value(forKey: key)
    }
    
    /**
    Save UIImage to cache data
     - Parameters:
        - image: UIImage to save in cache data
        - key:  String url of UIImage
     */
    func saveCacheData(of image: UIImage, for key: String) {
        let itemURL = NSString(string: key)
        cache.insertValue(image, forKey: key)
    }
}

extension UIImageView {

    /**
     Set image to UIImageView with Cached Image data or data from URL
     - Parameters:
        - urlString: String url of image
        - forceOption: Skip getting image from cache data and force to get image from url when true. default false
     */
    func loadImage(_ urlString: String?, forceOption: Bool = false) -> UIImage? {
        guard let imagePath = urlString else { return nil }
        
        // Cached Image is available
        if let cachedImage = ImageCacheManager.shared.loadCachedData(for: imagePath), forceOption == false {
            return cachedImage
            
            // No Image Cached
        } else {
            guard let imageURL = URL(string: imagePath) else { return nil }
            guard let imageData = try? Data(contentsOf: imageURL) else { return nil }
            guard let newImage = UIImage(data: imageData) else { return nil }
            
            ImageCacheManager.shared.saveCacheData(of: newImage, for: imagePath)
            return newImage
        }
    }
    
    func setImage(_ urlString: String?, forceOption: Bool = false) {
        DispatchQueue.global().async {
            guard let image = self.loadImage(urlString, forceOption: forceOption) else { return }
            
            DispatchQueue.main.async {
                self.image = image
            }
        }
    }
}

这是自定义 UIImage,当设置新图像时取消图像加载任务:

/// UIImageView with async image loading functions
class CancelableImageView: UIImageView {
    
    /// Cocurrent image loading work item
    private var imageLoadingWorkItem: DispatchWorkItem?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    init() {
        super.init(frame: CGRect.zero)
    }
    
}

// MARK: Concurrent image loading method
extension CancelableImageView {
    
    /**
     Cancel current image loading task and Set image to UIImageView with Cached Image data or data from URL
     - Parameters:
        - urlString: String url of image
        - forceOption: Skip getting image from cache data and force to get image from url when true. default false
     */
    func setNewImage(_ urlString: String?, forceOption: Bool = false) {
        
        self.cancelLoadingImage()
        self.imageLoadingWorkItem = DispatchWorkItem { super.setImage(urlString, forceOption: forceOption) }
        self.imageLoadingWorkItem?.perform()
    }
    
    /// Cancel current image loading work
    func cancelLoadingImage() {
        DispatchQueue.global().async {
            self.imageLoadingWorkItem?.cancel()
        }
    }
}

这是该视图的 UITableViewCell:

class ChartTableViewCell: UITableViewCell {

    lazy var posterImageView = CancelableImageView().then {
        $0.contentMode = .scaleAspectFit
        $0.image = UIImage(named: "img_placeholder")
    }
    
    ... 

    override func prepareForReuse() {
        setPosterPlaceholderImage()
        super.prepareForReuse()
    }
}

extension ChartTableViewCell {
    
    /// Set poster imageView placeholder Image
    private func setPosterPlaceholderImage() {
        self.posterImageView.image = UIImage(named: "img_placeholder")
    }
    
    // MARK: Set Data
    func setData(rank: Int, movie: MovieFront) {
        
        rankLabel.text = "\(rank+1)"
        titleLabel.text = movie.title
        genreLabel.text = movie.genre
        releaseDateLabel.text = movie.releaseDate
        starRating.rating = movie.ratingScore/2
        ratingCountLabel.text = "(\(movie.ratingCount))"
        if let imagePath = movie.posterPath {
            posterImageView.setNewImage(APIService.configureUrlString(imagePath: imagePath))
        }
    }
}

这是 UITableViewDataSource (cellForRowAt):

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        guard let cell = tableView.dequeueReusableCell(withIdentifier: identifiers.chart_table_cell) as? ChartTableViewCell else {
            return UITableViewCell()
        }
        
        cell.setData(rank: indexPath.row, movie: viewModel.movieListData.value[indexPath.row])
        return cell
    }
帖子版权声明 1、本帖标题:首次加载时来自 URL 的 UITableViewCell 图像错误
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由alexmherrmann在本站《uitableview》版块原创发布, 转载请注明出处!
最新回复 (0)
  • \'使用值而不是数组对象中的索引\' 在数组中,不行。在对象中,可以。您的第三个示例(创建对象数组)将是一个很好的解决方案。您还可以将整个对象变成一个对象,然后使用循环遍历它

返回
作者最近主题: