具有可变数量的子视图的动态UITableViewCell高度

我有一个UITableViewCell ,它使用从远程源检索的数据动态填充。 它具有可变数量的子视图,基于单元格中表示的项目类型。 我通过使用NSMutableAttributedStringboundingRectWithSize:方法动态计算单元格内容的大小。 我获取返回的值并计算一个combinedHeight变量,该变量存储在NSMutableDictionary并与单元格的indexPath相关联。 一切正常,但是当我尝试在heightForRowAtIndexPath:方法中使用字典中的值时,它只使用第一组可见单元格,之后一切都为0。

我的问题是我如何动态调整单元格的大小以包含所有子视图。 我还在单元格的contentView中添加了一个自定义视图,其宽度小于单元格,这样我就可以实现我们想要的独特风格。 这就是我们想要的风格。

这是我的代码:

  - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { if (areFeedItemsLoaded && feedItems.count > 0 && indexPath.row < feedItems.count) { CGFloat height = [[_cellHeights objectForKey:[self keyForIndexPath:indexPath]] floatValue]; return MAX(90, height); } return 44; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell; if (areFeedItemsLoaded) { if (feedItems.count == 0) { if (indexPath.row == 0) { cell = [EmptyTableViewCell getBlankCell:tableView]; } else { EmptyTableViewCell *eCell = [EmptyTableViewCell getEmptyCell:tableView withHeaderText:@"No Recent Activity"]; [eCell setLinkText:@"Your team's activity on Hudl will appear here." withUrl:nil]; cell = eCell; } } else if (indexPath.row  0) { if (thumbnailsView == nil) { thumbnailsView = [[UIView alloc] init]; thumbnailsView.tag = thumbnailTagId; [customContentView addSubview:thumbnailsView]; } else { for (UIView *view in thumbnailsView.subviews) { [view removeFromSuperview]; } } thumbnailsView.frame = CGRectMake(minX, CGRectGetMaxY(titleLabel.frame) + 8, width, 40); combinedHeight += 48; for (int i = 0; i < 3 && i < item.uris.count; i++) { NSURL *url = [item.uris objectAtIndex:i]; UIImageView *imageView = [[UIImageView alloc] init]; int x = i * 60 + i * 8; imageView.frame = CGRectMake(x, 0, 60, 40); [imageView setImageWithURL:url placeholderImage:[UIImage imageNamed:@"soft-dark-grad.png"]]; [thumbnailsView addSubview:imageView]; } } if (descriptionLabel == nil) { descriptionLabel = [[UILabel alloc] init]; descriptionLabel.tag = descriptionTagId; descriptionLabel.font = [UIFont systemFontOfSize:14]; descriptionLabel.textColor = [UIColor colorWithR:204 g:204 b:204 a:1]; descriptionLabel.numberOfLines = 0; descriptionLabel.lineBreakMode = NSLineBreakByWordWrapping; [customContentView addSubview:descriptionLabel]; } NSMutableAttributedString *descriptionText = [[NSMutableAttributedString alloc] initWithString:item.descriptionText]; [descriptionText addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"Helvetica" size:14] range:NSMakeRange(0, item.descriptionText.length)]; descriptionLabel.attributedText = descriptionText; CGSize descriptionSize = [descriptionText boundingRectWithSize:CGSizeMake(width, 10000) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading context:nil].size; descriptionLabel.frame = CGRectMake(minX, CGRectGetMaxY(thumbnailsView.frame) + 8, ceil(descriptionSize.width), ceil(descriptionSize.height)); combinedHeight += ceil(descriptionSize.height) + 8; } else { if (thumbnailsView != nil) { [thumbnailsView removeFromSuperview]; } if (descriptionLabel != nil) { [descriptionLabel removeFromSuperview]; } } UILabel *messageLabel = (UILabel *)[cell viewWithTag:messageTagId]; if ([item.feedItemType intValue] == MessageFI) { if (messageLabel == nil) { messageLabel = [[UILabel alloc] init]; messageLabel.tag = messageTagId; messageLabel.font = [UIFont systemFontOfSize:14]; messageLabel.textColor = [UIColor colorWithR:204 g:204 b:204 a:1]; messageLabel.numberOfLines = 0; messageLabel.lineBreakMode = NSLineBreakByWordWrapping; [customContentView addSubview:messageLabel]; } NSMutableAttributedString *messageText = [[NSMutableAttributedString alloc] initWithString:item.message]; [messageText addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"Helvetica" size:14] range:NSMakeRange(0, item.message.length)]; CGSize messageSize = [messageText boundingRectWithSize:CGSizeMake(width, 10000) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading context:nil].size; messageLabel.frame = CGRectMake(minX, CGRectGetMaxY(titleLabel.frame) + 8, ceil(messageSize.width), ceil(messageSize.height)); messageLabel.attributedText = messageText; combinedHeight += ceil(messageSize.height) + 8; } else { if (messageLabel != nil) { [messageLabel removeFromSuperview]; } } if ([item.feedItemType intValue] == PlaylistFI || [item.feedItemType intValue] == PlaylistAndNotesFI || [item.feedItemType intValue] == ArticleFI) { cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.selectionStyle = UITableViewCellSelectionStyleBlue; } else { cell.accessoryType = UITableViewCellAccessoryNone; cell.selectionStyle = UITableViewCellSelectionStyleNone; } [_cellHeights setObject:@(combinedHeight) forKey:[self keyForIndexPath:indexPath]]; CGRect frame = customContentView.frame; frame.size.height = MAX(70, combinedHeight); customContentView.frame = frame; } else if (!requestFailed) { cell = [LoadingTableViewCell getLoadingCell:tableView]; } else { cell = [ErrorTableViewCell getErrorCell:tableView]; } } else { cell = (indexPath.row == 0 || indexPath.row < feedItems.count) ? [EmptyTableViewCell getBlankCell:tableView] : [LoadingTableViewCell getLoadingCell:tableView]; } return cell; } - (NSIndexPath *)keyForIndexPath:(NSIndexPath *)indexPath { if ([indexPath class] == [NSIndexPath class]) { return indexPath; } return [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section]; } 

代码的问题是计算高度的顺序。 如果在两个方法中设置断点,您将看到tableView:heightForRowAtIndexPath:实际上被称为BEFORE tableView:cellForRowAtIndexPath: . 这意味着在设置单元格时进行高度计算将不起作用(因为高度在设置之前将返回0)。

这是我的建议:

创建一个UITableViewCell子类,并移动你在tableView:cellForRowAtIndexPath:所有初始化和设置逻辑tableView:cellForRowAtIndexPath:在该子类中。

一旦设置了一个单元格,任何数据属性可能会改变其高度,请在内部设置单元格的帧高,因为您将在下一步中使用它。

然后,为了独立计算高度,请在单元格本身中使用如下所示的方法:

 + (CGFloat)heightForCellWithWithData:// use whatever parameters are usually set on a cell that might affect it's height { static dispatch_once_t once; static CustomCellClass *sizingCell; // use your custom cell class here dispatch_once(&once, ^{ sizingCell = [[self alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"sizingCell"]; }); // set cell data properties here return sizingCell.frame.size.height; } 

然后获得你将要做的高度:

 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { if (areFeedItemsLoaded && feedItems.count > 0 && indexPath.row < feedItems.count) { return [CustomCellClass heightForCellWithWithData:whateverData]; } return 44; }