包含内嵌UITableView的UITableViewCell如何实现动态自适应高度
解决方案
- 修正错误配置
首先删除self.contentView.translatesAutoresizingMaskIntoConstraints = NO;这行代码,系统默认会管理contentView的约束,手动关闭会导致高度计算失效。
- 修正错误配置
- 自定义自动高度适配的内部TableView
创建SPDAutoHeightTableView子类,自动监听contentSize变化更新自身高度:
- 自定义自动高度适配的内部TableView
// SPDAutoHeightTableView.h @interface SPDAutoHeightTableView : UITableView @property (nonatomic, copy) void (^onHeightChanged)(CGFloat newHeight); @end // SPDAutoHeightTableView.m @implementation SPDAutoHeightTableView - (void)setContentSize:(CGSize)contentSize { [super setContentSize:contentSize]; // 内容大小变化时更新高度 for (NSLayoutConstraint *constraint in self.constraints) { if (constraint.firstAttribute == NSLayoutAttributeHeight) { constraint.constant = contentSize.height; if (self.onHeightChanged) self.onHeightChanged(contentSize.height); break; } } [self invalidateIntrinsicContentSize]; } - (CGSize)intrinsicContentSize { return self.contentSize; } @end
- 修正Cell内的约束和回调配置
修改Cell初始化代码,增加高度变更回调,完善table2的约束添加逻辑:
- 修正Cell内的约束和回调配置
// Cell.h 中新增回调属性 @property (nonatomic, copy) void (^onCellHeightNeedUpdate)(void); // Cell.m 实现 - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { self.openSection = -1; SPDSpinnerView *spinner = [[SPDSpinnerView alloc] initWithFrame:self.contentView.bounds]; spinner.label.text = @"Loading..."; spinner.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; // 加载视图加在contentView上 [self.contentView addSubview:spinner]; __weak typeof(self) weakSelf = self; [self loadDataWithCompletion:^(BOOL success) { __strong typeof(weakSelf) strongSelf = weakSelf; // 移除加载动画 for (UIView *view in strongSelf.contentView.subviews) { if ([view isKindOfClass:[SPDSpinnerView class]]) [view removeFromSuperview]; } if (success) { // 用自定义的自动高度TableView SPDAutoHeightTableView *table2 = [[SPDAutoHeightTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; table2.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; [table2 registerClass:[SPDChangelogHeaderView class] forHeaderFooterViewReuseIdentifier:@"headerView"]; table2.scrollEnabled = NO; table2.delegate = strongSelf; table2.dataSource = strongSelf; table2.translatesAutoresizingMaskIntoConstraints = NO; strongSelf.table2 = table2; // 高度变化回调 table2.onHeightChanged = ^(CGFloat newHeight) { if (strongSelf.onCellHeightNeedUpdate) { strongSelf.onCellHeightNeedUpdate(); } }; [UIView transitionWithView:strongSelf.contentView duration:.25 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{ [strongSelf.contentView addSubview:table2]; // 加全约束 [NSLayoutConstraint activateConstraints:@[ table2.topAnchor.constraintEqualToAnchor:strongSelf.contentView.topAnchor, table2.leadingAnchor.constraintEqualToAnchor:strongSelf.contentView.leadingAnchor, table2.trailingAnchor.constraintEqualToAnchor:strongSelf.contentView.trailingAnchor, table2.bottomAnchor.constraintEqualToAnchor:strongSelf.contentView.bottomAnchor, // 初始高度约束,后续会自动更新 table2.heightAnchor.constraintEqualToConstant:0 ]]; } completion:^(BOOL finished) { // 添加完成后刷新table2数据触发高度计算 [table2 reloadData]; }]; } else { // 自行添加错误提示Label的约束,避免用系统textLabel导致的布局问题 UILabel *errorLabel = [[UILabel alloc] init]; errorLabel.text = @"数据加载失败"; errorLabel.translatesAutoresizingMaskIntoConstraints = NO; [strongSelf.contentView addSubview:errorLabel]; [NSLayoutConstraint activateConstraints:@[ errorLabel.centerXAnchor.constraintEqualToAnchor:strongSelf.contentView.centerXAnchor, errorLabel.centerYAnchor.constraintEqualToAnchor:strongSelf.contentView.centerYAnchor, errorLabel.topAnchor.constraintEqualToAnchor:strongSelf.contentView.topAnchor constant:20, errorLabel.bottomAnchor.constraintEqualToAnchor:strongSelf.contentView.bottomAnchor constant:-20 ]]; if (strongSelf.onCellHeightNeedUpdate) { strongSelf.onCellHeightNeedUpdate(); } } }]; } return self; } // 点击分区头展开收起后,调用table2的reloadData触发contentSize更新即可 - (void)headerTapped:(UITapGestureRecognizer *)gesture { NSInteger section = gesture.view.tag; self.openSection = self.openSection == section ? -1 : section; [self.table2 reloadData]; }
- 在Table1的数据源中处理高度更新回调
在table1的cellForRowAtIndexPath方法中绑定cell的回调,触发table1的高度刷新:
- 在Table1的数据源中处理高度更新回调
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.row == 0) { // 第一个内嵌table2的cell SPDCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SPDCustomCell" forIndexPath:indexPath]; __weak typeof(tableView) weakTable = tableView; cell.onCellHeightNeedUpdate = ^{ __strong typeof(weakTable) strongTable = weakTable; // 执行begin/endUpdates自动计算高度,不需要reload行 [strongTable beginUpdates]; [strongTable endUpdates]; }; return cell; } // 其他cell的逻辑 }
- 确保Table1的自动高度配置正确
确认table1已经开启自动高度:
- 确保Table1的自动高度配置正确
self.table1.rowHeight = UITableViewAutomaticDimension; self.table1.estimatedRowHeight = 100; // 设为接近的预估高度即可
内容的提问来源于stack exchange,提问作者Arainty
相关产品推荐
相关产品推荐

