如何动态计算自定义展开/可折叠 UITableViewCell 的高度

Posted

技术标签:

【中文标题】如何动态计算自定义展开/可折叠 UITableViewCell 的高度【英文标题】:How to calculate the height of a custom expand/collapsable UITableViewCell dynamically 【发布时间】:2017-10-16 18:07:58 【问题描述】:

我有一个有趣的场景。我有一些在单独的 xib 文件中设计的自定义 UITableViewCells。其中一个有一个 UIImageView,它会加载大小不恒定的图像,因此这意味着 UIImageView 的高度必须是灵活的。该单元格还有一个包含一些 UILabel 的 UIView,并且 UIView 的大小是恒定的,例如 100。我想在 didselectrowatindexpath 事件上展开和折叠单元格。要折叠单元格,我必须隐藏具有一些标签的 UIView。请在这方面指导我以实现目标。 我的问题也是“如何计算单元格展开和折叠时行的高度。”谢谢

编辑:这是我尝试过的。 . .但是失败了

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
    static DynamicTableViewCell *cell = nil;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^
        cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    );
    
    [self setUpCell:cell atIndexPath:indexPath];
    
    CGFloat cellHeight = [self calculateHeightForConfiguredSizingCell:cell];
    if(cellHeight >0) 
        if(cell.isExpanded) 
            
            return cellHeight;
        
        else
        
            return (cellHeight - detailViewHeight);      // cell.detailView.frame.size.height = 100
        

    
    else 
        return cellHeight;
    

【问题讨论】:

给我们看一些代码。到目前为止,您尝试过什么? 【参考方案1】:

首先,出于性能原因,您不应该对单元格的实例有任何引用。

其次,您应该使用模型以正确的方式构建您的细胞。提供的代码根本不显示模型存储的使用情况。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
    // Say you have a array of models NSArray<YourModel *> *dataSource;

    YourModel *model = dataSource[indexPath.row]; // hope you have only one section.

第三,这是使用任何架构(如 MVVM、VIPER 或 MVC)的好方法,因为如果您没有架构,您可能会在未来的产品支持方面遇到问题。所以在 MVVM 的情况下,YourModel 就像 ViewModel。

要定义动态高度单元格的状态,请使用属性isExpanded。这是一个很好的观点,但它应该在另一个地方定义 - YourModel。如果你以适当的方式做到这一点,你实际上会知道没有细胞的细胞状态。

@interface YourModel ...
@property BOOL isExpanded;
@end

确保您在 didSelect 中正确更改了您的状态:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
    YourModel *model = dataSource[indexPath.row];
    model.isExpanded = !model.isExpanded; // inverse works like switch
    // Then relayout your row
    [tableView beginUpdates];
    // I commented next line because it might not be needed for you
    // Test it please
    // [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
    [tableView endUpdates]; 

所以回到 heightForRow:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
    // Say you have a array of models NSArray<YourModel *> *dataSource;

    YourModel *model = dataSource[indexPath.row]; // hope you have only one section.

    // It's better to extract next code to function or any class, but not in your YourModel.
    CGSize imageSize = [model.image doSizeCalulation]; // Use your algorithm to calculate image size
    CGSize shortText = [model.shortText sizeWithAttributes:@[/* font attributes */]];

    // If the cell is expanded then we should calculate height of text else height is zero.
    CGSize fullText = model.isExpanded ? [model.fullText sizeWithAttributes:@[/* font attributes */]]; : 0; 

    // just sum
    return imageSize.height + shortText.height + fullText.height;

实现此目的的另一种方法是使用UITableViewAutomaticDimension(只需在 heightForRow 方法中返回它)。在这种情况下,您应该正确设置约束,并在运行时根据 isExpanded 属性更改全文高度约束的常量。

【讨论】:

嗨,Alexander Shitikov,我衷心感谢您如此详细的回答。我是 ios 新手,您如此详细的回复对我很有帮助。还有一些小事情要回答,我想改变单元格的高度以对 didselectrowatindexpath 产生折叠/展开效果。在这方面也请指导我。 我也无法理解只有我的单元格引用了 IBOutlets 的 imageView 和标签。那么如何实现CGSize imageSize = [model.image doSizeCalulation];,接下来,您还说isExpanded属性应该在模型中,我的问题是“那我将如何在didselectrowatindexpath函数中更新它。请您提供我的一些示例代码. 我会很感激的。 您好!请再看一下答案,有一个带有 didSelectRow 示例的代码块,它清楚地说明了您应该如何更新 isExpanded 属性。 @BlackJaguar 最好将图像路径存储在模型中。像这样:@property (nonatomic, strong) NSString *image; 然后你可以使用[UIImage imageWithContentsOfFile:model.image]; 来获取 UIImage * 的实例并使用它的大小你可以计算你的图像。如果你真的很关心你的表现,你应该存储在模型图像元数据(宽度、高度、方向......)中以计算单元格的大小,而无需创建 UIImage * 的实例。 @BlackJaguar 您不需要 IBOutlets 和单元格 UI 组件的实例。主要思想是计算单元格的大小,而无需仅使用模型数据创建其实例。

以上是关于如何动态计算自定义展开/可折叠 UITableViewCell 的高度的主要内容,如果未能解决你的问题,请参考以下文章

展开和折叠 tableview 单元格

自定义展开 Segue

如何根据动态内容和最大尺寸限制折叠/展开视图?

如何使用 Bootstrap-Vue 从父组件折叠/展开多个动态折叠?

Bootstrap + Angular动态菜单在展开后不折叠

如何在 SwiftUI 中结合可折叠行使用 List 自定义行内部内容?