如何正确地在iPhone上进行可变高度的表格单元格?

时间:2009-09-18 08:57:53

标签: iphone uitableview

我的应用程序需要具有可变高度表格单元格(因为每个表格单元格的高度不同,而不是每个单元格都需要能够自行调整大小)。

我有一个目前有效的解决方案,但它很麻烦而且很慢。

我目前的解决方案:

在表格单元格渲染之前,我通过在其数据上调用-sizeWithFont:constrainedToSize:等大小调整方法来计算每个单元格需要多高。然后我将高度加起来,允许一些填充并将结果存储在数据中。

然后,当我的UITableViewDelegate收到-tableview:heightForRowAtIndexPath:时,我会计算出为该单元格呈现的项目并返回我之前计算的高度。

正如我所说的,这是有效的,但是当您按顺序执行数百个项目时调用-sizeWithFont:constrainedToSize:非常慢,我觉得可以做得更好。

因此,为了实现这一点,我必须维护两部分代码 - 一部分用于计算单元格高度,另一部分用于在时间到来时实际绘制单元格。

如果关于模型项的任何更改,我不得不更新这两个代码块,现在又一次它们仍然不能完美匹配,有时会导致表格单元格对于给定项目而言略微过小或者太大了。

我建议的解决方案:

所以我想取消预先计算细胞高度。 A)因为它打破了MVC范式而B)因为它很慢。

因此,我的细胞自我吸收,因此,最终得到正确的细胞高度。我的问题是我无法告诉表格在绘制之前查看单元格的高度 - 到时候为时已晚。

我尝试在-cellForRowAtIndexPath:内调用-tableView:heightForRowAtIndexPath:,但这会陷入无限循环,因为第一个在某个时刻调用第二个,反之亦然(至少这是我在看到的时候)尝试过。)

所以这个选项是不可能的。

如果我没有在行委托方法的高度中指定大小,那么表视图就会变得棘手。细胞是完美的高度,但它们的x位置是固定高度的细胞。

Messed Table Cells http://jamsoftonline.com/images/messed_table_cells.png

注意底部单元格的大小是正确的 - 它只是与前一个单元格重叠,而前一个单元格与之前的单元格重叠,依此类推。

同样使用这种方法,在滚动时会出现一些伪像,我认为这些伪像可能与单元格的重用标识符有关。

因此,非常感谢任何帮助。

3 个答案:

答案 0 :(得分:38)

这是我使用的。 NSString有一个方法,它会根据你给它的字体信息和高度/宽度限制告诉你文本框的尺寸。

- (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.
}

然后你写一个方法拉这个单元格的文本......

- (NSString *)getTextForIndexPath:(NSIndexPath *)indexPath
{
    NSString *sectionHeader = [self.tableSections objectAtIndex:[indexPath section]];
    NSString *sectionContent = [self.tableData objectForKey:sectionHeader];

    return sectionContent;
}

这是为了获得文本的大小。

- (CGSize)getSizeOfText:(NSString *)text withFont:(UIFont *)font
{
    return [text sizeWithFont:font constrainedToSize:CGSizeMake(280, 500)];
}

答案 1 :(得分:2)

只是一个想法:

  • 如果您有六种不同类型的单元格,每种单元格都有自己的标识符和固定高度,那该怎么办?一个用于单线电池,另一个用于双线电池等......

  • 每次模型更改时,计算该行的高度,然后找到最接近您所需高度的最近单元格类型。使用模型保存该celltype标识符。你还可以在模型中存储该单元格的固定行高,这样你就可以在tableview中返回它:heightForRowAtIndexPath调用(我不会太忙于强制它在单元类内部计算 - 技术上它不是单元格绘制功能以及tableview用于决定创建哪种单元格类型的更多内容。

  • 在运行时,当要求返回该行的单元格时,您需要做的就是创建(或从单元缓存中获取)具有单元格类型标识符的单元格,加载值并且您很高兴去

如果单元格高度计算太慢,那么您可以使用tableview缓存执行的相同技巧,并且仅在单元格进入视图时按需执行。在任何给定的时间,您只需要为视图中的单元格执行此操作,然后仅在单个单元格中滚动到两端的视图中。

答案 2 :(得分:0)

我知道由于你提到的无限循环,这对你不起作用,但我在调用单元格layoutSubViews方法方面取得了一些成功

虽然由于多次调用cellForRowAtIndexPath和layoutSubViews,这可能会有点低效,但我发现代码更清晰。

-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyCell *cell = (MyCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath];

    [cell layoutSubviews];

    return CGRectGetHeight(cell.frame);
}

在布局代码中:

- (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)];
}