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
:”有什么区别?`
我认为单元格配置可以在cellForRowAtIndexPath
或willDisplayCell: forRowAtIndexPath:
"中完成!
【问题讨论】:
类似于viewWillAppear
和viewDidlAppear
。
不错的答案!两者太相似了!
基本上,您在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
。比如说 - 你正在下载任何图像数据,那么这是进行下载或调用下载图像数据的方法的最佳位置。
在cellForRow
或cellForItem
中下载图像数据的问题是,大量图像/照片从初始请求中返回。 用户可能永远不会向下滚动足够远来查看所有图像。这是非常昂贵的,因为它可能会占用用户的蜂窝数据。
所以willDisplayCell
的大票是用户可能永远不会向下滚动,那么为什么要下载cellForRow
中的所有图像呢?使用 willDisplayCell 仅下载用户即将看到的图像。
这样你supply the cell in cellForRowAtIndexPath
和update the content in willDisplayCell
还要注意先调用cellForRow,然后调用willDisplayCell。
【讨论】:
除非单元格即将出现,否则不会调用 cellForItem 委托方法。如果数据源中有 20 项,则 cellForItem 不会自动连续调用 20 次。它可能会被调用 3 次,然后在下一个单元格出现之前再次调用,在下一个单元格出现之前再次调用,依此类推。WillDisplayCell 在 cellForItem 之后立即调用。以上是关于iOS UITableView:“cellForRowAtIndexPath”和“willDisplayCell:forRowAtIndexPath:”有啥区别的主要内容,如果未能解决你的问题,请参考以下文章
从 UIViewController 将变量传递给自定义 UITableViewCell
为啥在 heightForRowAt 方法中我的单元格为零?