iOS UITableView:“cellForRowAtIndexPath”和“willDisplayCell:forRowAtIndexPath:”有啥区别

Posted

技术标签:

【中文标题】iOS UITableView:“cellForRowAtIndexPath”和“willDisplayCell:forRowAtIndexPath:”有啥区别【英文标题】:iOS UITableView: what's the different between "cellForRowAtIndexPath" and "willDisplayCell: forRowAtIndexPath:"iOS UITableView:“cellForRowAtIndexPath”和“willDisplayCell:forRowAtIndexPath:”有什么区别 【发布时间】:2015-08-13 12:36:35 【问题描述】:

正如问题的标题所提到的: “cellForRowAtIndexPath”和“willDisplayCell:forRowAtIndexPath:”有什么区别?`

我认为单元格配置可以在cellForRowAtIndexPathwillDisplayCell: forRowAtIndexPath:"中完成!

【问题讨论】:

类似于viewWillAppearviewDidlAppear 不错的答案!两者太相似了! 基本上,您在cellForRowAtIndexPath 中向系统提供一个单元格(配置取决于您),但willDisplayCell: forRowAtIndexPath: 是系统将单元格发送给您的一种方式,如果您想在最后一刻进行更改(再由你决定)。 另见Proper place to update UITableViewCell content 【参考方案1】:

你是对的,细胞配置(理论上)可以在这两种方法中完成。

然而,几乎所有UITableView 都有一个实现cellForRowAtIndexPath: 的数据源(这是协议中的必需方法)。另一方面,willDisplayCell:forRowAtIndexPath:(它是 delegate 的方法,而不是数据源)是可选的。

由于配置单元通常取决于您要显示的数据cellForRowAtIndexPath: 是迄今为止进行单元配置的最常见位置。 (我什至不记得使用willDisplayCell:forRowAtIndexPath:)。

有一个值得注意的例外:当您使用情节提要和 静态 单元格(而不是单元格原型)时,您无法在 cellForRowAtIndexPath: 中做任何有用的事情(因为 dequeueReusableCellWithIdentifier:返回nil),所以你必须在willDisplayCell:forRowAtIndexPath:viewWillAppear:或其他方法中进行配置。

@NSDeveloper:你是对的。感谢您的提示。

【讨论】:

使用 [super tableView:tableView cellForRowAtIndexPath:indexPath] 而不是 dequeue... 来获取静态单元格。 这在 ios 10 中变得很重要,collectionView(默认情况下)会提前预取单元格。正如 Apple 所说:注意:启用预取时,collectionView:cellForItemAtIndexPath: 集合视图委托上的方法会在需要单元格时提前调用。为避免视觉外观不一致,请使用 collectionView:willDisplayCell:forItemAtIndexPath: 委托方法更新单元格以反映视觉状态,例如选择。【参考方案2】:

cellForRowAtIndexPath 实际上应该返回一个单元格实例。如果可能,它应该是一个重复使用的单元格。 UITableViewDataSource 协议需要此方法。这通常是您选择将在单元格中显示的数据的地方。对于动态单元格,通常在设置数据的同时设置 UI 属性(例如选定状态)。

willDisplayCell 是可选的,在之后调用。这是您在显示单元格之前自定义单元格的最后机会。此时,单元实例已经创建。您可以在此处更改选定状态等内容。您不应更改单元格的数据/结构或实例化任何新内容,而应仅更改单元格的 UI 属性的状态。这通常用于静态单元格。

【讨论】:

Apples Lister 示例代码是该用法的一个很好的例子developer.apple.com/library/ios/samplecode/Lister/Introduction/… 还有一个关于该主题的不错的博客blog.lazerwalker.com/objective-c/code/2013/12/01/… 有点错别字,cellForRowAtIndexPath 属于UITableViewDataSource,而不是UITableViewDelegate【参考方案3】:

我认为这回答了你的问题:

但是非常重要的事情仍然存在: tableView:cellForRowAtIndexPath: 方法,应该是 在 UITableView 的 dataSource 中实现,为每个单元格调用和 应该工作得很快。所以你必须尽快返回重用的单元格实例 尽可能。

此时不要执行数据绑定,因为屏幕上还没有单元格。为此,您可以使用 tableView:willDisplayCell:forRowAtIndexPath:方法可以 在 UITableView 的委托中实现。准确调用的方法 在 UITableView 的边界中显示单元格之前。

From Perfect smooth scrolling in UITableViews

【讨论】:

有没有实验数据证明这两者的区别? 在iOS 9中没有区别,但可能和iOS 10有区别。tech.zalando.com/blog/… 我认为tableView:willDisplayCell:forRowAtIndexPath: 中的绑定数据没有帮助。我测试过,看不出有什么区别。我已经修复了color blended layer,取消选中clear context,检查了opaque,但这些都不起作用。我认为问题在于autolayout 太慢了。【参考方案4】:

尽管看起来很直观,但 willDisplay cell: cellForRowAt indexPath: 被调用之后立即被调用。

我有一个应用程序,其中图像和视频将从 URLCache 加载或下载并显示在单元格中。我注意到每当我启动我的应用程序时,所有的视频和图像都会在我看到它们之前加载,我注意到了这一点,因为我可以在查看 tableView 中的第一项时听到 tableView 中最后一个视频的音频。这是 tableView 中的 8 个项目。我打印到控制台以更好地了解委托函数调用的顺序。

结果:

在第 0 行创建单元格 在第 0 行绘制 feed 单元 在第 1 行创建单元格 在第 1 行绘制 feed 单元 在第 2 行创建单元格 在第 2 行绘制 feed 单元 在第 3 行创建单元格 在第 3 行绘制 feed 单元 在第 4 行创建单元格 在第 4 行绘制 feed 单元 在第 5 行创建单元格 在第 5 行绘制 feed 单元 在第 6 行创建单元格 在第 6 行绘制 feed 单元 在第 7 行创建单元格 在第 7 行绘制馈送单元 停止在第 2 行显示单元格 停止在第 3 行显示单元格 停止在第 4 行显示单元格 停止在第 5 行显示单元格 停止在第 6 行显示单元格 停止在第 7 行显示单元格

守则:

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let cell = tableView.dequeueReusableCell(withIdentifier: "feedCell", for: indexPath) as! FeedCell
    print("Created cell at row \(indexPath.row)")
    let post = posts[indexPath.row]
    cell.post = post
    cell.viewController = self
    cell.configureCell()
    return cell


override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) 
    if let feedCell = cell as? FeedCell
        print("Drew feed cell at row \(indexPath.row)")
        // only want download task to start when the cell will display
        if feedCell.post.isVideo 
            feedCell.loadVideo()
         else 
            feedCell.loadImage()
        
    


override func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) 
    print("Stopped showing cell at row \(indexPath.row)")
    if let feedCell = cell as? FeedCell
        feedCell.player?.pause()
    

【讨论】:

你能分享你的饲料单元的代码sn-p吗?你是如何创建用户界面的。 willDisplay 并不总是在每行的 cellForRow 之后立即调用。例如。横向滚动到大表格底部,旋转到纵向,那些需要填充纵向屏幕的额外单元格都先用cellForRow批量调用,然后再调用willDisplayCell。【参考方案5】:

我在使用自动布局和UITableViewAutomaticDimension 时遇到了willDisplayCell: 中的单元格配置问题。重复使用的单元格的高度计算不正确。 NSLog中的打印方法显示willDisplayCell:是在heightForRowAtIndexPath:之后调用的(在iOS 10.2模拟器上测试过)

cellForRowAtIndexPath: <NSIndexPath: 0x7c10daa0> length = 2, path = 1 - 0
heightForRowAtIndexPath: <NSIndexPath: 0x7c10daa0> length = 2, path = 1 - 0
heightForRowAtIndexPath: <NSIndexPath: 0x7c10daa0> length = 2, path = 1 - 0
willDisplayCell: <NSIndexPath: 0x7c10daa0> length = 2, path = 1 - 0
cellForRowAtIndexPath: <NSIndexPath: 0x7c4516f0> length = 2, path = 1 - 1
heightForRowAtIndexPath: <NSIndexPath: 0x7c4516f0> length = 2, path = 1 - 1
heightForRowAtIndexPath: <NSIndexPath: 0x7c4516f0> length = 2, path = 1 - 1
willDisplayCell: <NSIndexPath: 0x7c4516f0> length = 2, path = 1 - 1

我认为是这个原因,因为将配置代码放入cellForRowAtIndexPath:后问题就消失了

附: @iTSangar 发布的链接和引用有一篇相反的文章:https://tech.zalando.com/blog/proper-use-of-cellforrowatindexpath-and-willdisplaycell/

【讨论】:

这是主要/唯一的区别。当您更改 willDisplayCell: 中的任何会更改单元格高度的内容时,自动高度无法正常工作。理想情况下,完全忽略willDisplayCell:,并在需要UITableView.automaticDimension 时将所有内容设置在cellForRowAtIndexPath: 中。【参考方案6】:

每次单元格即将显示在屏幕上时,都会调用委托方法willDisplayCell。比如说 - 你正在下载任何图像数据,那么这是进行下载或调用下载图像数据的方法的最佳位置。

cellForRowcellForItem 中下载图像数据的问题是,大量图像/照片从初始请求中返回。 用户可能永远不会向下滚动足够远来查看所有图像。这是非常昂贵的,因为它可能会占用用户的蜂窝数据。

所以willDisplayCell 的大票是用户可能永远不会向下滚动,那么为什么要下载cellForRow 中的所有图像呢?使用 willDisplayCell 仅下载用户即将看到的图像。

这样你supply the cell in cellForRowAtIndexPathupdate the content in willDisplayCell

还要注意先调用cellForRow,然后调用willDisplayCell。

【讨论】:

除非单元格即将出现,否则不会调用 cellForItem 委托方法。如果数据源中有 20 项,则 cellForItem 不会自动连续调用 20 次。它可能会被调用 3 次,然后在下一个单元格出现之前再次调用,在下一个单元格出现之前再次调用,依此类推。WillDisplayCell 在 cellForItem 之后立即调用。

以上是关于iOS UITableView:“cellForRowAtIndexPath”和“willDisplayCell:forRowAtIndexPath:”有啥区别的主要内容,如果未能解决你的问题,请参考以下文章

从 UIViewController 将变量传递给自定义 UITableViewCell

为啥在 heightForRowAt 方法中我的单元格为零?

IOS-UITableView入门

iOS 编辑UITableView(根据iOS编程编写)

iOS小技能:1. 什么是UITableView? 2. UITableView如何展示数据

iOS开发之UITableView的使用