How can I do variable height table cells on the iPhone properly?
我的应用需要具有可变高度的表格单元格(因为每个表格单元格的高度不同,而不是每个单元格都需要能够自行调整大小)。
我有一个目前有效的解决方案,但它很笨拙而且速度很慢。
我目前的解决方案:
在表格单元格被渲染之前,我通过在其数据上调用诸如
然后,当我的 UITableViewDelegate 收到
正如我所说,这是可行的,但是当您按顺序为数百个项目执行此操作时,调用
因此,为了使其工作,我必须维护两部分代码 - 一部分用于计算单元格高度,另一部分用于在时机成熟时实际绘制单元格。
如果模型项发生任何变化,我必须更新这两个代码块,但有时它们仍然不能完美匹配,有时会导致表格单元格对于给定的项目,或太大。
我提出的解决方案:
所以我想取消预先计算单元格高度。 A) 因为它打破了 MVC 范式和 B) 因为它很慢。
所以我的单元格自行绘制,结果,单元格高度正确。我的问题是我无法在绘制之前告诉表格视图单元格的高度 - 到那时为时已晚。
我尝试从
所以这个选项是不可能的。
如果我没有为行委托方法指定高度的大小,那么表格视图就会变得混乱。单元格是完美的高度,但它们的 x 位置是固定高度的单元格的位置。
混乱的表格单元格 http://jamsoftonline.com/images/messed_table_cells.png
注意底部单元格的大小是正确的 - 它只是与前一个单元格重叠,前一个单元格与其前一个单元格重叠,依此类推。
同样使用这种方法,在滚动时会出现一些伪影,我认为这可能与单元格的重用标识符有关。
因此,我们将不胜感激。
这是我使用的。 NSString 有一个方法可以根据字体信息和您给它的高度/宽度约束告诉您文本框的尺寸。
1 2 3 4 5 6 7 8 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *text = [self getTextForIndexPath:indexPath]; UIFont *font = [UIFont systemFontOfSize:14]; CGSize size = [self getSizeOfText:text withFont:font]; return (size.height + 11); // I put some padding on it. } |
然后你写一个方法来拉取这个单元格的文本...
1 2 3 4 5 6 7 | - (NSString *)getTextForIndexPath:(NSIndexPath *)indexPath { NSString *sectionHeader = [self.tableSections objectAtIndex:[indexPath section]]; NSString *sectionContent = [self.tableData objectForKey:sectionHeader]; return sectionContent; } |
这是获取文本的大小。
1 2 3 4 | - (CGSize)getSizeOfText:(NSString *)text withFont:(UIFont *)font { return [text sizeWithFont:font constrainedToSize:CGSizeMake(280, 500)]; } |
只是一个想法:
-
如果你有六种不同类型的单元格,每个单元格都有自己的标识符和固定的高度。一个用于单行电池,另一个用于两行电池,等等...
-
每次您的模型发生变化时,计算该行的高度,然后找到高度最接近您需要的最近的单元格类型。将该单元类型标识符与模型一起保存。您还可以在模型中存储该单元格的固定行高,以便您可以在 tableview:heightForRowAtIndexPath 调用中返回它(我不会太担心强制它在单元格类本身内部进行计算——从技术上讲,它不是一部分的单元格绘图功能和更多的东西 tableview 用来决定创建哪种单元格类型)。
-
在运行时,当被要求为该行返回一个单元格时,您需要做的就是创建(或从单元格缓存中获取)具有单元格类型标识符的单元格,加载值,然后就可以开始了。
如果单元格高度计算太慢,那么您可以采用与 tableview 缓存相同的技巧,仅在单元格进入视图时按需执行。在任何给定时间,您只需对视图中的单元格执行此操作,然后只需对单个单元格执行此操作,因为它在任一端滚动到视图中。
由于您提到的无限循环,我意识到这对您不起作用,但是我在调??用单元格 layoutSubViews 方法方面取得了一些成功
虽然由于对 cellForRowAtIndexPath 和 layoutSubViews 的多次调用,这可能有点低效,但我发现代码更简洁。
1 2 3 4 5 6 7 8 | -(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { MyCell *cell = (MyCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath]; [cell layoutSubviews]; return CGRectGetHeight(cell.frame); } |
并在布局代码中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | - (void)layoutSubviews { [super layoutSubviews]; //First expand the label to a large height to so sizeToFit isn't constrained [self.myArbitrarilyLengthLabel setFrame:CGRectMake(self.myArbitrarilyLengthLabel.frame.origin.x, self.myArbitrarilyLengthLabel.frame.origin.y, self.myArbitrarilyLengthLabel.frame.size.width, 1000)]; //let sizeToFit do its magic [self.myArbitrarilyLengthLabel sizeToFit]; //resize the cell to encompass the newly expanded label [self setFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, CGRectGetMaxY(self.myArbitrarilyLengthLabel.frame) + 10)]; } |