繁体   English   中英

UITableView灵活/动态heightForRowAtIndexPath

[英]UITableView flexible/dynamic heightForRowAtIndexPath

案件

通常,您将使用cellForRowAtIndexPath委托方法来设置您的单元格。 为单元格设置的信息对于单元格的绘制方式以及大小的大小非常重要。

不幸的是,在cellForRowAtIndexPath委托方法之前调用heightForRowAtIndexPath委托方法,因此我们不能简单地告诉委托返回单元格的高度,因为那时它将为零。

因此,我们需要在表格中绘制单元格之前计算大小。 幸运的是,有一个方法就是这样, sizeWithFont ,属于NSString类。 然而,存在问题,为了动态地计算正确的大小,它需要知道如何呈现单元中的元素。 我将在一个例子中说明这一点:

想象一下UITableViewCell ,它包含一个名为textLabel的标签。 cellForRowAtIndexPath委托方法中,我们放置textLabel.numberOfLines = 0 ,它基本上告诉标签它可以有多少行,因为它需要呈现特定宽度的文本。 如果我们为textLabel提供一个大于最初赋予textLabel的宽度的文本,则会出现问题。 第二行将出现,但单元格的高度不会自动调整,因此我们得到一个混乱的查看表视图。

如前所述,我们可以使用sizeWithFont来计算高度,但它需要知道使用哪个Font,宽度等等。如果为了简单起见,我们只关心宽度,我们可以硬编码宽度将是大约320.0(不考虑填充)。 但是如果我们使用UITableViewStyleGrouped而不是普通的宽度那么将会发生大约300.0并且单元格将再次混乱。 或者如果我们从纵向交换到横向,我们有更多的空间,但由于我们硬编码300.0,它将不会被使用。

在这种情况下,你必须问自己一个问题,你可以避免多少硬编码。

我自己的想法

您可以调用属于UITableView类的cellForRowAtIndexPath方法来获取特定节和行的单元格。 我读了几篇帖子说你不想那样做,但我真的不明白。 是的,我同意它已经分配了单元格,但是仅对可见的单元heightForRowAtIndexPath委托方法,因此无论如何都将分配单元格。 如果正确使用dequeueReusableCellWithIdentifier ,则不会在cellForRowAtIndexPath方法中再次分配单元格,而是使用指针并仅调整属性。 那有什么问题呢?

请注意,单元格未在cellForRowAtIndexPath委托方法中绘制,当表格视图单元格变为可见时,脚本将调用UITableVieCell上的setNeedDisplay方法,该方法触发drawRect方法绘制单元格。 因此,直接调用cellForRowAtIndexPath委托不会失去性能,因为它需要绘制两次。

好的,通过在heightForRowAtIndexPath委托方法中调用cellForRowAtIndexPath委托方法,我们可以获得有关单元格的所有信息,以确定它的大小。

也许您可以创建自己的sizeForCell方法,该方法遍历所有选项,如果单元格为Value1样式,或者Value2等,该怎么办。

结论/问题

这只是我在思想中描述的理论,我想知道我写的是不是正确的。 或者也许还有另一种方法来完成同样的事情。 请注意,我希望能够尽可能灵活地做事。

是的,我同意它已经分配了单元格,但是仅对可见的单元格调用heightForRowAtIndexPath委托方法,因此无论如何都将分配单元格。

这是不正确的。 表视图需要为表视图中的所有行调用heightForRowAtIndexPath (如果已实现),而不仅仅是当前正在显示的行。 原因是它需要计算出它的总高度才能显示正确的滚动指示器。

我曾经这样做过:

  1. 根据将用于表视图的集合对象创建集合对象(大小信息数组(字典,行高的NSNumber等)。

  2. 当我们从本地或远程源处理数据时,就完成了这项工作。

  3. 当我创建这个集合对象时,我预先确定将要使用的字体的类型和大小。 您甚至可以存储UIFont对象或用于表示内容的任何自定义对象。

  4. 每次实现UITableViewDataSource或UITableViewDelegate协议时,都会使用这些集合对象来确定UITableViewCell实例及其子视图的大小等。

通过这种方式,您可以避免必须继承UITableViewCell只是为了获得其内容的各种大小属性。

不要使用绝对值来初始化帧。 使用基于当前方向和边界的相对值。

如果我们将它旋转到任何方向,只需在运行时执行调整大小机制。 确保正确设置了autoresizingMask。

您只需要高度,UITableViewCell中不需要所有不必要的东西来确定行高。 你可能甚至不需要宽度,因为我说宽度值应该相对于视图边界。

这是我解决这个问题的方法

  1. 我假设在这个解决方案中只有一个Label具有“动态”高度
  2. 我还假设,如果我们使标签自动尺寸拉伸高度,因为细胞增长只需更改细胞高度
  3. 我假设笔尖在标签所在的位置以及上方和下方有多少空间之间有适当的间距
  4. 每次我们更改笔尖中标签的字体或位置时,我们都不想更改代码

如何更新高度:

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    // We want the UIFont to be the same as what is in the nib,
    //  but we dont want to call tableView dequeue a bunch because its slow. 
    //  If we make the font static and only load it once we can reuse it every
    //  time we get into this method
    static UIFont* dynamicTextFont;
    static CGRect textFrame;
    static CGFloat extraHeight;
    if( !dynamicTextFont ) {
        DetailCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
        dynamicTextFont = cell.resizeLabel.font;
        CGRect cellFrame = cell.frame;
        textFrame = cell.resizeLabel.frame;
        extraHeight = cellFrame.size.height-textFrame.size.height; // The space above and below the growing field
    }
    NSString* text = .... // Get this from the some object using indexPath 

    CGSize  size = [text sizeWithFont:dynamicTextFont constrainedToSize:CGSizeMake(textFrame.size.width, 200000.f) lineBreakMode:UILineBreakModeWordWrap];

    return size.height+extraHeight;
}

问题:

  • 如果您没有使用原型单元格,则需要检查单元格是否为nil并初始化它
  • 您的笔尖/故事板必须具有UILabel自动调整大小并将多行设置为0

你应该看看TTTableItemCell.m在Three20框架。 它遵循一种不同的方法,基本上通过让每个单元类(使用一些预定义的设置,如字体,布局等)实现共享方法+ tableView: sizeForItem:或类似的东西),它传递项目对象中的文本。 当您查找特定单元格的文本时,您也可以查找相应的字体。

关于单元格高度:您可以检查tableView的宽度,并在必要时通过UITableViewStyleGrouped减去边距,并将宽度作为最终索引栏和公开项目(您在单元格数据的数据存储中查找)。 当tableView的宽度发生变化时,例如通过界面旋转,您必须调用[tableView reloadData]

我对动态细胞高度有所了解。

只需创建自定义单元格的一个实例作为UITableViewController成员变量。 tableView:heightForRowAtIndexPath:方法中设置单元格的内容并返回单元格的高度。

这样,如果在heightForRowAtIndexPath方法中调用cellForRowAtIndexPath则不会像创建/自动释放单元格那样多次。

UPD:为方便起见,您还可以在自定义单元格类中创建静态方法,该方法将创建用于高度计算的单例单元格实例,设置单元格的内容,然后返回其高度。

tableView:heightForRowAtIndexPath:函数体现在看起来像这样:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return [MyCell cellHeightForContent:yourContent];
}

为了回答原始海报问的问题是“可以调用cellForRowAtIndexPath吗?”,问题不然。 这会给你一个单元格,但它不会在内部将它分配给indexPath,也不会重新排队(没有方法可以将它放回去),所以你只会失去它。 我想它会在自动释放池中并最终被释放,但你仍然会一遍又一遍地创建大量的单元格,这真的很浪费。

你可以做动态的单元格高度,你甚至可以让它们看起来很漂亮,但真正使它们看起来很无缝是很多的工作,如果你想支持多个方向等等,那就更多了。

这是我用来为聊天应用程序实现一些相当光滑的单元格的解决方案。

到目前为止,我一直对heightForCellAtIndexPath感到非常恼火:因为它导致违反DRY原则。 有了这个解决方案,我的heightForRowAtIndexPath:每个单元花费1.5毫秒,我可以减少到约1毫秒。

基本上,您希望单元格内的每个子视图都实现sizeThatFits:创建一个您配置的屏幕外单元格,然后使用sizeThatFits查询根视图:CGSizeMake(tableViewWidth,CGFLOAT_MAX)。

沿途有一些问题。 一些UIKit视图具有昂贵的setter操作。 例如 - [UITextView setText]做了很多工作。 这里的技巧是创建一个子类,缓冲变量,然后覆盖setNeedsDisplay以在要渲染视图时调用 - [super setText:]。 当然,你必须实现自己的sizeThatFits:使用UIKit扩展。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM