【问题标题】:UIImageView does not initially display image in UITableViewCellUIImageView 最初不会在 UITableViewCell 中显示图像
【发布时间】:2016-10-29 18:05:08
【问题描述】:

我有一个从服务加载图像的UITableView。它获取 URL 并使用 NSURLSession.sharedSession().dataTaskWithURL 下载图像:

NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, _, error) -> Void in
                            guard
                                let data = data where error == nil,
                                let image = UIImage(data: data)
                                else {
                                    print(error?.localizedDescription)
                                    return
                            }
                            dispatch_async(dispatch_get_main_queue()) {
                                [unowned self] in
                                self.pictureView.image = image
                                self.pictureView.layoutIfNeeded()
                            }
                        }).resume()

UIImageView 是故事板场景的出口。 UIImageView 内容模式是 Aspect Fit。

可见单元格的UIImageView 最初显示为空(无图像),即使我已确认已设置图像。直到单元格在屏幕上滚动关闭/滚动,单元格才会重绘(?)并显示图像。

所以这似乎是某种时间问题,但我无法弄清楚。设置图像后,我尝试在 dispatch_async 块中调用以下所有内容:

self.pictureView.setNeedsDisplay()
self.pictureView.setNeedsUpdateConstraints()
self.pictureView.setNeedsLayout()
self.pictureView.reloadInputViews()

这些都不能解决问题。是什么导致此图像仅在单元格滚动出/进入视图后显示?

编辑: 值得注意的是,这是发生在 nib 和相关类中的。笔尖已加载,然后我开始下载。我想知道笔尖是否会自行布局,当我的图像被下载并设置在UIImageView 上时,布局通道已经完成。然后通过在屏幕上移动单元格(包含笔尖),单元格再次执行布局并正确显示所有内容。只是一个猜测,我仍然坚持这一点。

编辑 2: 进一步澄清流程:

1) 我的UIViewController 包含一个UITableView

2) 这个UITableView 从一个名为 PostCell 的自定义 UITableViewCell 类中加载单元格。

3) PostCell 的全部内容是从名为 PostView 的 nib 加载的单个视图(带有 PostView 类)。该负载发生在 PostCell 中的 init:style:reuseIdentifier 期间:

postView = NSBundle.mainBundle().loadNibNamed("PostView", owner: self, options: nil).first as? PostView

4) 然后我在postView 上设置一个 Post 对象:

postView.post = Post

请注意,这发生在 awakeFromNib() 在 PostView 上调用之后。

5) PostView 上的post 属性有一个didSet 观察者,它根据post 对象(上面的代码)中的URL 启动图像下载。图像被下载并设置在名为pictureViewUIImageView 上。

编辑 3:

这是tableView(_:cellForRowAtIndexPath:)的代码:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    var cell = tableView.dequeueReusableCellWithIdentifier(postCellIdentifier) as? PostCell

        if cell == nil {
           cell = PostCell()
        }

        if let postCell = cell,
            let postArray = postArray,
            let post = postArray[indexPath.row] {
            postCell.post = post
            return postCell
        }

        // This section is hit during a refresh.
        let emptyCell = UITableViewCell()
        emptyCell.contentView.backgroundColor = .whiteColor()
        return emptyCell

    }

编辑 4:

我发现如果我通过在笔尖中预先设置一个占位符图像来“填充”UIImageView,然后将我的图像加载到该UIImageView,图像将显示在占位符的高度。当我将该单元格拉出屏幕然后再次打开时,UIImageView 将使用笔尖中的约束设置以正确的大小绘制图像。

因此,UIImageView 的约束和大小似乎在图像加载时已设置,并且将其从屏幕上拉下/拉上会导致某种类型的重绘,从而正确调整图像的大小。如果我可以以编程方式触发重绘会有所帮助,但请参阅上面的内容了解我已经尝试过的内容。

编辑 6:

它正在发挥作用。请注意,我在笔尖中设置了一个占位符图像。这似乎导致图像在最初加载时显示,但在占位符的尺寸上。当它正确显示时,将其短暂滚动到屏幕外会导致调整大小。

编辑 7:

我在我的应用程序的其他地方使用 PostView nib 没有问题。当它加载图像大小正确。问题似乎只出现在 nib 嵌入 UITableViewCell 中。

编辑 8:

我找到了这个问题的根本原因,在我的UIViewController 的 viewDidLoad 中调用:

tableView.estimatedRowHeight = 500
tableView.rowHeight = UITableViewAutomaticDimension

使用自动标注会导致初始行高不正确。出于某种原因,将单元格滚动出/进入视图将正确重新计算行高,并且图像以正确的高度显示。删除此代码将显示图像,但行高不会从自动高度计算中受益。自动计算行高并避免此问题的最佳方法是什么?

【问题讨论】:

  • 你什么时候调用 dataTaskWithURL ?
  • @TejaNandamuri 在tableView:cellForRowAtIndexPath 中调用。我用dequeueReusableCellWithIdentifier 获取自定义UITableViewCell。然后我在那个单元格上设置一个对象,它有一个didSet 来获取图像。
  • self.pictureView.image = imagecell.pictureView.image = image ?
  • 张贴您的tableView:cellForRowAtIndexPath: 了解更多详情,请。
  • 您是否尝试过:tableView.beginUpdates(); tableView.endUpdates; 在异步块中?

标签: ios swift uitableview uiimageview


【解决方案1】:

你可以试试这个扩展:

extension UIImageView {
    func downloadedFrom(link link:String, contentMode mode: UIViewContentMode) {
        guard
            let url = NSURL(string: link)
            else {return}
        contentMode = mode
        NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
            guard
                let httpURLResponse = response as? NSHTTPURLResponse where httpURLResponse.statusCode == 200,
                let mimeType = response?.MIMEType where mimeType.hasPrefix("image"),
                let data = data where error == nil,
                let image = UIImage(data: data)
                else { return }
            dispatch_async(dispatch_get_main_queue()) { () -> Void in
                self.image = image
            }
        }).resume()
    }
}

然后你可以把这条线放在你想要的任何地方

cell.cellPhoto.downloadedFrom(link: "customImageUrl", contentMode: .ScaleAspectFit) //Try changing contentMode

【讨论】:

  • 问题似乎是UITableViewAutomaticDimension。请参阅我的问题中的编辑 8。
  • 在表格视图委托中使用 heightForRow 方法。因此,您将计算行高。如果此时没有图像,则使用默认高度,然后在获取图像时重新加载单元格行。
  • 不错的扩展伙伴!我只有一个问题。如果图像很大,它不会下载它并显示一个空白图像
  • 谢谢:)。你的图片有多大?
【解决方案2】:

你见过WWDC 2014 Session 226 吗?该视频可能会更详细地说明您的问题。

在 WWDC 会话中,他们使用 self.tableView.estimatedRowHeight = 100.0;你已经完成了✅ 然后他们说你不需要来自 table view controller 的任何类型的虚拟原型单元。

然后的问题是,当情节提要加载表格视图时,它会将rowHeight 属性设置为来自 xib/interface builder 的值。要使用自动调整单元格大小,您需要设置 estimatedRowHeight 并将 rowHeight 保留为默认值 - UITableViewAutomaticDimension。 (你做过的)✅

self.tableView.rowHeight = UITableViewAutomaticDimension

⚠️新问题是第一个单元格在您获得真正的行高之前加载。诀窍可能是在视图显示时强制重新加载表?

[super viewDidAppear:animated]
[self.tableView reloadData]

如果这不起作用,那么您可能无意中在导致其尺寸不正确的东西上设置了 explicit contentWidth/Height ❓❓

【讨论】:

    【解决方案3】:

    下载完成后,您需要重新加载您的 tableview 数据。尝试在dispatch_async 块内调用self.tableview.reloadData()

    另外,如果您想从 URL 异步下载图片,我建议您使用 Kingfisher

    【讨论】:

    • 我确实尝试过,但没有帮助。问题似乎是UITableViewAutomaticDimension。请参阅我的问题中的编辑 8。
    【解决方案4】:

    查看响应者的编辑过程(目前为 8 个)和更新后,您似乎在 init 方法中遇到了导致errors in init mentioned here 的相同错误。

    应该感谢 Prcela 在注册笔尖方面走在正确的轨道上,但下面提供了一个更优雅的解决方案:

    在 nib 文件中创建自定义表格视图单元格的最简单方法(自 iOS 5.0 起可用)是在表格视图控制器中使用 registerNib:forCellReuseIdentifier:。最大的优点是 dequeueReusableCellWithIdentifier: 然后在必要时自动从 nib 文件中实例化一个单元格。您不再需要 if (cell == nil) ... 部分。

    viewDidLoad下的Table View Controller中

    [self.tableView registerNib:[UINib nibWithNibName:@"CueTableCell" bundle:nil] forCellReuseIdentifier:@"CueTableCell"];
    

    cellForRowAtIndexPath添加

    CueTableCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CueTableCell"];
    // setup cell
    return cell;
    

    从 nib 文件加载的单元格使用 initWithCoder 进行实例化,您 如有必要,可以在您的子类中覆盖它。对于修改 UI 元素,你应该重写 awakeFromNib (不要忘记 叫“超级”)。

    我希望我能将这样一个优雅的解决方案归功于我,但这个功劳属于同一帖子中的 Martin R

    【讨论】:

    • 问题在于更新自定义单元格中的图像最初不会触发布局的更新。 UIImageView 直到它被拉出屏幕,然后在屏幕上才会刷新,此时会发生重绘(?)并显示图像。如果我在UIImageView 上预设了占位符图像,我下载的图像将显示占位符图像高度,并且仅在它离开屏幕后才调整大小,然后在屏幕上。这个问题的解决方案是如何在下载后以编程方式触发调整大小。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-24
    • 2011-07-09
    相关资源
    最近更新 更多