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

时间:2014-04-02 18:14:41

标签: ios objective-c uitableview

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

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

这是我的代码:

    - (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];
}

1 个答案:

答案 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;
}