Swift:在 UITableViewCell 中异步加载图像 - 自动布局问题

Posted

技术标签:

【中文标题】Swift:在 UITableViewCell 中异步加载图像 - 自动布局问题【英文标题】:Swift: loading image asynchronously in UITableViewCell - Autolayout issue 【发布时间】:2019-06-06 09:27:26 【问题描述】:

在我的 UITableViewCell 中,我有一个 UIImageView 异步下载图像(使用 Kingfisher 库)。它与 Instagram 非常相似。

我正在使用自动布局,我想使用UITableView.automaticDimension 来表示行高。我应该如何设置约束以使单元格的高度根据图像变得更高或更短?如果我为UIImageView 设置初始约束并在下载图像后更改它,我至少有一个元素会更改其高度以“填充”UIImageView 添加(或删除)的空间。

【问题讨论】:

您需要根据图片的高度手动计算每个单元格的高度。 那真是一团糟。但是,我应该在哪里设置新高度? 你必须在 tableView(_:heightForRowAt:) 方法中传递它。 实际高度是在单元格内部和调用 tableView(_:heightForRowAt:) 之后计算的。每次刷新整个tableView是唯一的选择吗? 是的。这是下载图像后根据图像获取单元格高度的唯一选项。否则,您必须在 API 本身中接受图像的 aspectRatio。 【参考方案1】:

使用图像的实际尺寸和设备屏幕尺寸计算纵横比保留高度,并更新 UITableViewCell 上的 UIImageView 高度约束。

只需在 tableView(_:cellForRowAt:) 上调用 feedCell.configure(with: FeedImage)

// FeedImage data will be load from your API, in the best practices, you should get actual sizes of the image from your API

struct FeedImage 
    var url: String = ""
    var width: CGFloat = 0.0
    var height: CGFloat = 0.0


class FeedCell: UITableViewCell 

    @IBOutlet weak var feedImageView: UIImageView!
    @IBOutlet weak var feedImageViewHeightConstraint: NSLayoutConstraint!

    override func awakeFromNib() 
        super.awakeFromNib()
        // Initialization code
    

    override func setSelected(_ selected: Bool, animated: Bool) 
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    

    func configure(with feedImage: FeedImage) 

        feedImageViewHeightConstraint.constant = getAspectRatioPreservedHeight(actualWidth: feedImage.width, actualHeight: feedImage.height)

        feedImageView.loadImage(with: feedImage.url) // Asynchronous image downloading and setting, you can use any library such as SDWebImage, AlamofireImage, Kingfisher or your custom asynchronous image downloader

    

    // Calculate the aspect ratio preserved height using with actual size of the images and device screen sizes.
    private func getAspectRatioPreservedHeight(actualWidth: CGFloat, actualHeight: CGFloat) -> CGFloat 

        let WIDTH = UIScreen.main.bounds.width
        let HEIGHT = UIScreen.main.bounds.height

        if (actualWidth == 0)  return CGFloat(400.0) 

        let newComputedHeight = (WIDTH*actualHeight)/actualWidth

        if newComputedHeight > (HEIGHT * 0.6) 
            return (HEIGHT * 0.6)
        

        return newComputedHeight
    


【讨论】:

我就是这样做的,但是单元格的高度不正确,因为其他项目会拉伸以适合我在情节提要中定义的单元格的原始高度。 为 UITableView 行高设置 UITableViewAutomaticDimension,并为估计的行高设置单元格的近似高度 不是直接的。我的解决方法是让我的 API 在下载之前向我发送图像大小,这样我就可以在设置单元格时设置 UIImageView 的高度。它起作用了,但只是因为我有机会编辑 API。 是的,你是在正确的方式:) 你也应该下载图像的裁剪版本以适应 uiimageview。要下载的图片的尺寸必须提供与计算高度相同的比例。【参考方案2】:

您必须按比例裁剪图像,然后保存到 KingFisher 缓存。

let cached = ImageCache.default.imageCachedType(forKey: url)
        if cached != .none 
            getImageFromCache(key: url)  [weak self] (cachedImage) in
                self?.image = cachedImage
                self?.completionHandler?(cachedImage)
            
        
        else 
            getImageFromServer(key: url)  [weak self] (cahcedImage) in
                self?.image = cahcedImage
                ImageCache.default.store(cahcedImage, forKey: self!.url)
                self?.completionHandler?(cahcedImage)
            
        





 func getImageFromCache(key:String,complition:@escaping (UIImage)->()) 
        ImageCache.default.retrieveImage(forKey: url, options: nil)  [weak self] (image, cachetype) in

            guard let cachedImage = image , let opCancelled = self?.isCancelled , opCancelled == false  else 
                return
            
            complition(cachedImage)
        
    

    func getImageFromServer(key:String,complition:@escaping (UIImage)->()) 
        if let imageUrl = URL(string: key) 
            KingfisherManager.shared.retrieveImage(with: imageUrl, options: nil, progressBlock: nil)  [weak self] (image, error, cachetype, _ ) in
                if error == nil 
                    guard let cachedImage = image , let opCancelled = self?.isCancelled , opCancelled == false  else 
                        return
                    


                        let finalheight = (UIScreen.main.bounds.width * CGFloat(cachedImage.size.height)) / CGFloat(cachedImage.size.width)
                        let newImage = cachedImage.resize(targetSize: CGSize(width: UIScreen.main.bounds.width, height: finalheight))
                        complition(newImage)

                
                else 
                    print("Error ###### == Errror While Downloading Image")
                
            
        
        else 
            print("Error ###### == can't create Url of String")
        
    

【讨论】:

以上是关于Swift:在 UITableViewCell 中异步加载图像 - 自动布局问题的主要内容,如果未能解决你的问题,请参考以下文章

在嵌入在 UITableviewCell 中的 UICollectionView 中进行 Segue,Swift

Swift交替UITableViewCell渐变颜色

如何在 Swift 中检测对 UITableViewCell 的 UIImageView 的触摸?

UITableViewCell 中的 UICollectionView 在 swift 3 中滚动不流畅

如何在 Swift 3 中将 UIButton 添加到我的 UITableViewCell 中?

UITableViewCell 不会在 swift 中保持突出显示