简体   繁体   English

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

[英]Async loading image from URL into UIImageView in cell doesn't load on initial load

I am downloading an image from a URL then setting the image to my imageView when the image has downloaded (see code below). 我正在从URL下载图像,然后在图像下载后将图像设置为我的imageView(请参见下面的代码)。 I am using a custom UITableViewCell with a UIImageView setup with NSAutoLayoutConstraints . 我正在将自定义UITableViewCell与带有NSAutoLayoutConstraintsUIImageView设置一起NSAutoLayoutConstraints The constraints properly set the size of the cell, but the cell only displays the image if the cell is off the screen or if the user scrolls to reload that cell. 约束条件正确设置了单元格的大小,但是只有当该单元格不在屏幕上或者用户滚动以重新加载该单元格时,该单元格才会显示图像。

Here is the code: 这是代码:

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
}}

So essentially, once the image is downloaded then method is called on the main thread. 因此,从本质上讲,一旦下载了映像,便会在主线程上调用方法。 Yet the cell doesn't actually resize itself. 然而,单元实际上并没有调整自身大小。 The cell is the same height it was when the table originally loaded. 单元格的高度与表格最初加载时的高度相同。 In my case, since the image view is nil, the height is only 30 points high due to the margins above/below the image view. 在我的情况下,由于图像视图为零,因此由于图像视图上方/下方的边距,高度仅高了30点。 If I set a placeholder then that is replaced yet the image is still not replaced. 如果我设置了一个占位符,则该占位符将被替换,但图像仍不会被替换。 The cell size only displays correctly if it is off the screen or if the user scrolls. 仅当单元大小不在屏幕上或用户滚动时,它才能正确显示。 I've seen a few threads about this, but many are quite old. 我已经看到了一些与此相关的主题,但是其中许多主题已经很老了。 Is this still a bug on Apple's part or is there a solution? 这仍然是Apple的错误,还是有解决方案? I have tried setNeedsLayout() as well as other methods to try and force an update on the constraints but nothing has worked. 我已经尝试过setNeedsLayout()以及其他方法来强制对约束进行更新,但是没有任何效果。 Any ideas/solutions from anyone? 任何人的任何想法/解决方案?

EDIT: 编辑:

The code that calls setCustomImage is as follows: 调用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)
                }
            }

I added code to reload the row when the image is set which now works. 我添加了代码,可以在设置图像后重新加载该行,现在可以正常工作了。 layoutIfNeeded() did not do anything. layoutIfNeeded()没有执行任何操作。 The network call to download the image returns the image on the main thread so that's not an issue. 下载图像的网络调用在主线程上返回了图像,所以这不是问题。 Once the cell is set I tried calling layoutIfNeeded() to no avail. 设置单元格后,我尝试将layoutIfNeeded()无效。 Specifically reloading the row is the only thing that cures it. 专门重新加载该行是唯一可以解决该问题的方法。 I just didn't know if this was inefficient or not. 我只是不知道这是否效率低下。

You need to call layoutIfNeeded which will layout subviews. 您需要调用layoutIfNeeded将布局子视图。 Calling setNeedsLayout should be redundant in your case since adding the constraint itself should already call that internally. 在您的情况下,调用setNeedsLayout应该是多余的,因为添加约束本身应该已经在内部调用了。

There is no bug in this situation, you can't expect the whole window hierarchy is repositioned and redrawn just because one of your image is being downloaded. 在这种情况下没有错误,您不能期望整个窗口层次结构都被重新定位和重绘,因为仅下载了一张图像。

Saying "but the cell only displays the image if the cell is off the screen" is what a tester does. 测试人员会说“如果单元格不在屏幕上,那么单元格只会显示图像”。 You need to analyze your issue and identify: 您需要分析问题并确定:

  • Is cell size correct after the image is set? 设置图像后,像元大小是否正确?
  • Is image view size and position correct after the image is set? 设置图像后图像视图的尺寸和位置是否正确?
  • Is image on image view correct after assigning it? 分配后,图像视图上的图像是否正确?

From your description I see a few most probable candidates: 从您的描述中,我看到了一些最有可能的候选人:

  • You are not updating UI on main thread 您不是在主线程上更新UI
  • Your cell is internally not layout correctly (call layoutIfNeeded ) 您的单元格内部布局不正确(调用layoutIfNeeded
  • Your cell needs to resize (on table view call beginUpdates + endUpdates ) 您的单元格需要调整大小(在表视图上调用beginUpdates + endUpdates
  • You are having issues on which cell you are calling setCustomImage due to cell dequeuing (table view cell and async data load is a bit more complex then what you seem to have built) 由于单元出队,您在调用setCustomImage单元上遇到问题(表视图单元和异步数据加载比您似乎构建的要复杂一些)

Try these points and hopefully one of the first will solve your issue. 尝试这些要点,希望其中第一个可以解决您的问题。 If not then make sure you update your question by showing how these methods are being called. 如果不是,请确保通过显示如何调用这些方法来更新您的问题。

Although there are many tools to achieve this but setting remote images in table view cells should look something like this: 尽管有许多工具可以实现此目的,但是在表视图单元格中设置远程图像应如下所示:

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
    }

}

实际上,您实际上需要reload row at specific IndexReload TableView reload row at specific Index正如您提到的,当滚动tableView或正确显示在屏幕外的Cell时,一切都按预期工作

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM