当单元格尚未构建时,如何计算 heightForRowAtIndexPath?

发布于 2024-10-21 22:25:08 字数 655 浏览 4 评论 0原文

问题 - 如何最好地计算 UITableViewController 的“heightForRowAtIndexPath”方法中一行的高度,假设:

  1. 我正在使用自定义子类 UITableViewCell &子视图(例如 UILabels)的实际大小是在运行时计算的取决于诸如用户是否更改了字体大小之类的事情,
  2. 单元格实际上并未准备好,它似乎在“heightForRowAtIndexPath”之前,因此您不能依赖于调用特定的自定义单元格即时来查询它

我唯一能想到的目前是: 1. 在您的自定义 UITableViewCell 子类中创建一个方法来计算 UITableViewCell 子类中每个子视图(例如 UILabel)的高度 - 然后在创建实例时在单元格子类中使用它 2. 另外,在自定义子类中创建一个类方法,该方法遍历所有 UILabels,调用上述方法来总结高度,从而计算出总行高。它必须获取传递给它的数据(例如每个 UILabels 中的文本) 3. 在 UITableViewController“heightForRowAtIndexPath”中,您必须调用上面 (2) 中的“calRowHeight”类型方法,并向其传递标签文本数据。因此,有效地调用自定义单元格子类上的类方法,该方法知道如何计算总行高,但它也使用单元格所需的相同逻辑...

是否有比我缺少的更简单的方法?

Question - How does one best calculate the height for a row in the "heightForRowAtIndexPath" method of a UITableViewController, given that:

  1. I'm using a custom subclassed UITableViewCell & the actually size of the subview (e.g. UILabels) is calculated at runtime & dependant on things such as if the user changed the font size
  2. the cell's aren't actually prepared it seems prior to a "heightForRowAtIndexPath", so you can't rely on calling your specific custom cell instant to query it

Only thing I can think of for the moment is to:
1. In your custom UITableViewCell subclass create a method that calculates the heights of each subview (e.g. UILabel) that is in the UITableViewCell subclass - then use this within the cell subclass when it is creating instances
2. Also in the custom subclass create a class method that runs through all the UILabels, calling the above-mentioned method, to sum up the heights and therefore work out the total row height. It would have to get the data passed to it (e.g. text in each of the UILabels)
3. In the UITableViewController "heightForRowAtIndexPath" then you have to call the "calRowHeight" type method from (2) above, passing it the label text data. So effectively call a class method on your custom cell subclass which knows how to work out the total row height, but it's using the same logic that the cell needs too...

Is there an easier way than this I'm missing?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

灼疼热情 2024-10-28 22:25:08

创建 UITableView 时,每当您向其发送 reloadData 消息时,都会向数据源发送每个单元格的 heightForRowAtIndexPath 消息。因此,如果您的表格有 30 个单元格,则该消息将发送 30 次。

假设这 30 个单元格中只有 6 个在屏幕上可见。在这种情况下,当创建时并向其发送 reloadData 消息时,UITableView 将为每个可见行发送一个 cellForRowAtIndexPath 消息,即该消息被发送六次。

苹果为什么要这样实现呢?部分原因是计算行的高度几乎总是比构建和填充整个单元格更便宜。鉴于在许多表格中每个单元格的高度都是相同的,因此它通常要便宜得多。部分原因是因为 iOS 需要知道整个表格的大小:这允许它创建滚动条并将其设置在滚动视图等上。

如果您的行高大小不同,因为它们包含不同数量的文本,您可以对相关字符串使用 sizeWithFont: 方法之一来进行计算。这比构建视图然后测量结果更快。请注意,如果更改单元格的高度,则需要重新加载整个表格(使用 reloadData - 这将询问委托的每个高度,但仅询问可见单元格)或有选择地重新加载大小已改变的行改变了。

附加材料
如果我理解评论中的后续问题,以下内容可能会有所帮助:

如果您正在实现编辑模式,那么需要更改表格行的高度并不罕见。例如,您的表格行中可能有文本,当它们的单元格变窄时(为了为右侧的删除圆圈腾出空间),您可能希望某些单元格变高以容纳文本。这里的基本方法是:

  • 确保 tableView:heightForRowAtIndexPath: 方法知道您是否处于编辑模式。 (它可以使用 isEditing 询问 tableView。)然后根据您是否处于编辑模式获取返回正确高度的方法。

  • 在 UITableViewController 中的 setEditing:animated: 方法中(或 UIViewController,无论您使用的是哪种 - 根据您使用的不同,会有一些差异,因此值得仔细检查文档),之后发送一条 reloadData 消息到 tableView改变了它的状态。这将强制 tableView 获取每一行的高度,并将重新获取可见行的单元格。当您进入编辑模式时,tableView 会处理使单元格变窄,但如果您想在布局上做更多工作,请在 tableView:cellForRowAtIndex: 中进行。如上所述,总体策略是找到一种快速计算高度的方法。使用文本 sizeWithFont: (及其变体)可以做到这一点。如果您有图像等,那么您可以获取它们的尺寸并进行一些求和。

  • 除了这些步骤之外,您可能还想在切换模式后稍微滚动 tableView。如果行的高度不同,那么切换模式后您将最终处于表格中的错误位置。我在这里采取的方法是在重新加载表后使用 PerformSelector:withObject:afterDelay 来调用执行滚动调整的方法。您需要使用延迟,以便 tableView 有时间收集新的高度和新的表格单元格。 (可能有一种更聪明的方法。)我根据重新加载之前和之后屏幕上单元格第一个可见行的 tableView:cellForRowAtIndexPath: 的 origin.y 之间的差异进行一些求和,以进行滚动调整。因此,例如,要获取预加载之前的位置,有点像这样。

    CGPoint offset = [[self tableView] contentOffset];
    NSIndexPath* indexPath = [[self tableView] indexPathForRowAtPoint:CGPointMake(0,offset.y)];
    CGFloat preCellOffset = [[[self tableView] cellForRowAtIndexPath:indexPath] origin].y;
    

When a UITableView is created and whenever you send it a reloadData message, the datasource is sent one heightForRowAtIndexPath message for each cell. So if your table has 30 cells, that message gets sent 30 times.

Say only six of those 30 cells are visible on screen. In that case, when created and when you send it a reloadData message, the UITableView will send one cellForRowAtIndexPath message per visible row, i.e. that message gets sent six times.

Why do Apple implement it like this? Part of the reason is that it's almost always cheaper to calculate the height of a row than it is to build and populate a whole cell. And given that in many tables the height of every cell will be identical, it is often vastly cheaper. And part of the reason is because iOS needs to know the size of the whole table: this allows it to create the scroll bars and set it up on a scroll view etc.

If your row heights vary in size because they hold varying amounts of text, you can use one of the sizeWithFont: methods on the relevant string to do the calculations. This is quicker than building a view and then measuring the result. Note, that if you change the height of a cell, you will need to either reload the whole table (with reloadData - this will ask the delegate for every height, but only ask for visible cells) OR selectively reload the rows where the size has changed.

Additional material
If I understand the follow up question in the comment, the following may help:

If you are implementing an editing mode, then it's not uncommon to need to change the height of your table rows. For example, you may have text in your table rows and when they cells become narrower - to make space for the delete circles on the right - you may want some of the cells to become taller to accommodate the text. The basic approach here is to:

  • Make sure the tableView:heightForRowAtIndexPath: method knows whether your are in editing mode or not. (It can ask the tableView using isEditing.) And then get the method to return the right height, depending on whether you are in editing mode or not.

  • In your setEditing:animated: method in the UITableViewController (or a UIViewController, whichever you are using - there are some differences depending what you use, so it's worth checking the documentation carefully) send a reloadData message to the tableView after you have changed its state. This will force the tableView to grab the heights of every row and it will refetch the cells for the visible rows. The tableView handles making cells narrower when you enter editing mode, but if you want to do more work on the layout, do it in tableView:cellForRowAtIndex:. As noted above, the general strategy is to find a means of calculating the height that is quick. With text sizeWithFont: (and its variants) can do it. If you have images etc., then you can grab their dimensions and do some sums.

  • In addition to those steps you may also want to scroll the tableView a bit after switching modes. If the heights of your rows are different, then you will end up in the wrong position in the table after switching mode. An approach I have taken here is to use performSelector:withObject:afterDelay after I've reloaded the table to call a method that does the scroll adjusting. You need to use the delay, to allow time for the tableView to collect the new heights and the new table cells. (There may be a smarter way of doing this.) I do some sums to make the scroll adjustment based on the difference between the origin.y of the tableView:cellForRowAtIndexPath: of the cell first visible row on screen before and after the reload. So, for e.g., to get the position before the pre-load, something a bit like this.

    CGPoint offset = [[self tableView] contentOffset];
    NSIndexPath* indexPath = [[self tableView] indexPathForRowAtPoint:CGPointMake(0,offset.y)];
    CGFloat preCellOffset = [[[self tableView] cellForRowAtIndexPath:indexPath] origin].y;
    
清浅ˋ旧时光 2024-10-28 22:25:08

我过去所做的(我不确定是否是最有效的)是在我的 heightForRowAtIndexPath 方法中调用 cellForRowAtIndexPath 然后我向视图询问该单元格的高度。我对页眉和页脚高度做了类似的事情。这样,如果我更改单元格、页眉或页脚,我不必记得去更新相应的高度方法。

What I have done in the past, which I am not sure is the most efficient, is from within my heightForRowAtIndexPath method call cellForRowAtIndexPath then I ask the view for that cell its height. I have done similar things for header and footer heights. This way if I change the cell, the header, or the footer, I don't have to remember to go and update the corresponding height method.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文