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

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

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

【问题讨论】:

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

标签: ios swift uitableview autolayout


【解决方案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")
        }
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-04
    • 1970-01-01
    • 1970-01-01
    • 2017-02-13
    • 2016-06-27
    相关资源
    最近更新 更多