我有一个UITableViewCell
,它使用从远程源检索的数据动态填充。它具有可变数量的子视图,基于单元格中表示的项目类型。我正在使用NSMutableAttributedString
和boundingRectWithSize:
方法动态计算单元格内容的大小。我获取返回的值并计算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 < feedItems.count)
{
FeedItem *item = [feedItems objectAtIndex:indexPath.row];
cell = [self.tableView dequeueReusableCellWithIdentifier:FeedItemCellIdentifier forIndexPath:indexPath];
if (cell == nil)
{
cell = [[FeedItemTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:FeedItemCellIdentifier];
}
cell.clipsToBounds = YES;
cell.backgroundColor = [UIColor clearColor];
CGFloat combinedHeight = 8;
UIView *customContentView = [cell viewWithTag:customContentTagId];
if (customContentView == nil)
{
customContentView = [[UIView alloc] init];
customContentView.tag = customContentTagId;
customContentView.backgroundColor = [UIColor colorWithR:100 g:100 b:100 a:1];
[cell.contentView addSubview:customContentView];
}
customContentView.frame = CGRectMake(50, 0, 270, 70);
UIImageView *userImage = (UIImageView *)[cell viewWithTag:userImageTagId];
if (userImage == nil)
{
userImage = [[UIImageView alloc] init];
userImage.tag = userImageTagId;
[customContentView addSubview:userImage];
}
userImage.frame = CGRectMake(8, 8, 45, 45);
userImage.layer.cornerRadius = CGRectGetWidth(userImage.frame) / 2;
userImage.layer.masksToBounds = YES;
NSURL *imageUrl = item.imageSourceUrl;
[userImage setImageWithURL:imageUrl placeholderImage:[UIImage imageNamed:@"default-user.png"]];
CGFloat minX = CGRectGetMaxX(userImage.frame) + 8;
UILabel *titleLabel = (UILabel *)[cell viewWithTag:titleTagId];
if (titleLabel == nil)
{
titleLabel = [[UILabel alloc] init];
titleLabel.numberOfLines = 0;
titleLabel.textColor = [UIColor colorWithR:204 g:204 b:204 a:1];
titleLabel.font = [UIFont fontWithName:@"Helvetica" size:14];
titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
titleLabel.tag = titleTagId;
[customContentView addSubview:titleLabel];
}
NSString *title = [NSString stringWithFormat:@"%@ %@", item.creatorName, item.title];
NSMutableAttributedString *titleText = [[NSMutableAttributedString alloc] initWithString:title];
[titleText addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithR:204 g:204 b:204 a:1] range:NSMakeRange(0, title.length - 1)];
[titleText addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"Helvetica-Bold" size:14] range:NSMakeRange(0, item.creatorName.length)];
[titleText addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"Helvetica" size:15] range:NSMakeRange(item.creatorName.length + 1, title.length - item.creatorName.length - 1)];
titleLabel.attributedText = titleText;
CGFloat width = CGRectGetWidth(customContentView.frame) - (CGRectGetMaxX(userImage.frame) + 8) - 8;
width = MIN(width, 200);
CGSize titleSize = [titleText boundingRectWithSize:CGSizeMake(width, 10000) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading context:nil].size;
titleLabel.frame = CGRectMake(minX, CGRectGetMinY(userImage.frame), ceil(titleSize.width), ceil(titleSize.height));
combinedHeight += ceil(titleSize.height) + 8;
UIView *thumbnailsView = [cell viewWithTag:thumbnailTagId];
UILabel *descriptionLabel = (UILabel *)[cell viewWithTag:descriptionTagId];
if ([item.feedItemType intValue] == PlaylistFI || [item.feedItemType intValue] == PlaylistAndNotesFI)
{
if ([item.uris count] > 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];
}
答案 0 :(得分:3)
代码的问题是计算高度的顺序。如果您在两种方法中设置断点,您将看到tableView:heightForRowAtIndexPath:
实际上被称为tableView:cellForRowAtIndexPath:
。这意味着在设置单元格时进行高度计算将不起作用(因为高度在设置之前将返回0)。
以下是我的建议:
创建一个UITableViewCell
子类,并移动您在该子类中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;
}