将URL中的图像异步加载到单元格中的UIImageView中并不会在初始加载时加载

时间:2018-05-23 02:06:29

标签: ios swift xcode

我正在从URL下载图像,然后在下载图像时将图像设置为我的imageView(请参阅下面的代码)。我使用自定义UITableViewCellUIImageView设置为NSAutoLayoutConstraints。约束条件正确设置了单元格的大小,但如果单元格不在屏幕上,或者用户滚动重新加载该单元格,则单元格仅显示图像。

以下是代码:

final class ImageTableViewCell: UITableViewCell {

static let reuseIdentifier = "Image Cell"

@IBOutlet var postImageView: UIImageView!

internal var aspectConstraint : NSLayoutConstraint? {
    didSet {
        if oldValue != nil {
            postImageView.removeConstraint(oldValue!)
        }
        if aspectConstraint != nil {
            postImageView.addConstraint(aspectConstraint!)
        }
    }
}

override func prepareForReuse() {
    super.prepareForReuse()
    aspectConstraint = nil
}

func setCustomImage(image : UIImage) {
    let aspect = image.size.width / image.size.height
    let constraint = NSLayoutConstraint(item: postImageView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: postImageView, attribute: NSLayoutAttribute.height, multiplier: aspect, constant: 0.0)
    constraint.priority = UILayoutPriority(rawValue: 999)

    aspectConstraint = constraint

    postImageView.image = image
}}

基本上,一旦下载了图像,就会在主线程上调用方法。然而,细胞实际上并没有调整自身的大小。单元格与表格最初加载时的高度相同。在我的情况下,由于图像视图为零,由于图像视图上方/下方的边距,高度仅为30点高。如果我设置了一个占位符,那么它被替换,但图像仍未被替换。如果单元格大小不在屏幕上或者用户滚动,则单元格大小只能正确显示。我已经看到了一些关于这个的线索,但很多都很老了。这仍然是苹果公司的一个错误,还是有解决方案?我已尝试setNeedsLayout()以及其他方法尝试强制更新约束但没有任何效果。任何人的想法/解决方案?

编辑:

调用setCustomImage的代码如下:

The code that calls setCustomImage is the following:

Network.shared.downloadImage(url: post.postURL) { image, error in
                if let image = image {
                    cell.setCustomImage(image: image)

                    // reload row
                    self.tableView.reloadRows(at: [cell.indexPathForCell!], with: .fade)
                } else if let error = error {
                    print(error.localizedDescription)
                }
            }

我添加了代码以在设置现在有效的图像时重新加载行。 layoutIfNeeded()没有做任何事情。下载图像的网络调用返回主线程上的图像,因此不是问题。一旦设置了单元格,我尝试调用layoutIfNeeded()无效。特别是重新加载行是治愈它的唯一方法。我只是不知道这是否效率低下。

2 个答案:

答案 0 :(得分:0)

您需要调用layoutIfNeeded来布局子视图。在你的情况下调用setNeedsLayout应该是多余的,因为添加约束本身应该已经在内部调用它。

在这种情况下没有错误,您不能指望整个窗口层次结构重新定位并重新绘制,因为正在下载您的某个图像。

说“但是如果单元格不在屏幕上,则单元格仅显示图像”是测试人员所做的事情。您需要分析您的问题并确定:

  • 设置图像后单元格大小是否正确?
  • 设置图像后图像视图大小和位置是否正确?
  • 分配后图像视图上的图像是否正确?

根据您的描述,我看到了一些最可能的候选人:

  • 您没有在主线程上更新UI
  • 您的单元格内部布局不正确(调用layoutIfNeeded
  • 您的单元格需要调整大小(在表格查看电话beginUpdates + endUpdates
  • 由于单元格出列,您在调用哪个单元格setCustomImage时遇到问题(表视图单元格和异步数据加载比您似乎构建的要复杂得多)

尝试这些要点,希望首先解决您的问题。如果没有,请确保通过显示如何调用这些方法来更新您的问题。

虽然有很多工具可以实现这一点,但在表视图单元格中设置远程图像应该如下所示:

class MyCell: UITableViewCell {

    @IBOutlet private var modelImageView: UIImageView?

    var model: MyModel? {
        didSet {
            refresh()
        }
    }

    private var currentImageURL: URL? // Track what the current image URL is
    func refresh() {
        if model?.imageURL != currentImageURL { // Only do changes if another URL is being set
            let url = model?.imageURL // We need to save this to check it in block
            modelImageView?.image = nil // Clear the current image for cases of dequeuing
            ImageTools.fetchImage(url, completion: { image in
                if url == model?.imageURL { // Due to asynchronousy we need to check if this is really the image we need to show
                    self.currentImageURL = url // Save the new url
                    self.modelImageView.image = image // Assign the image
                }
            })
        }
        // Setup other UI here
    }

}

答案 1 :(得分:0)

您实际上需要reload row at specific IndexReload TableView正如您所提到的那样,滚动tableView或Cell正确显示在屏幕外时,一切正常如预期