UITableViewCell 的动态高度和里面的内容

Posted

技术标签:

【中文标题】UITableViewCell 的动态高度和里面的内容【英文标题】:Dynamic Height For UITableViewCell and The content inside 【发布时间】:2013-09-24 15:15:15 【问题描述】:

我有一个特别的问题,我似乎无法解决我已经查看了我的代码几个小时,但似乎无法解决问题。

当我向我的表格视图添加消息时,消息会溢出 UITableViewCell 中的包装器,有时包装器会被单元格截断。

我如何确保我的包装器总是足够大以容纳 txt (lblMessage) 并且单元格足够大以容纳包装纸。

提前致谢!

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

    CGFloat height = [self heightForMessage:indexPath];

    if(height < 50)
    
        NSLog(@"Normal : %d , row : %d", 100,indexPath.row);
        return 100;
        
    else
    
        NSLog(@"Custom : %f , row : %d", 70 + height,indexPath.row);
        return 70 + height;
    


-(CGFloat)heightForMessage:(NSIndexPath *)indexPath

    MessageObject * model = [self.chats objectAtIndex:indexPath.row];

    CGSize size = [model.message sizeWithFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:12] constrainedToSize:CGSizeMake(290, 100000) lineBreakMode:NSLineBreakByWordWrapping];

    return size.height;


-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    static NSString * NewsCellIdentifer = @"NewsCellIdentifier";
    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:NewsCellIdentifer];

    UILabel * lblContact;
    UILabel * lblMessage;
    UILabel * lblDate;
    UIView * wrapper;

    if (cell == nil)
    

        /* Top menu for login */

        wrapper = [[UIView alloc] initWithFrame:CGRectMake(10, 5, 300, 90)];
        [wrapper setBackgroundColor:[self.delegate.color colorWithAlphaComponent:0.6f]];
        [wrapper.layer setCornerRadius:6.0f];
        [wrapper.layer setShadowOffset:CGSizeMake(0, 2)];
        [wrapper.layer setShadowRadius:2.0f];
        [wrapper.layer setShadowOpacity:0.5f];
        [wrapper viewWithTag:19];
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:NewsCellIdentifer];

        /* Contact Name */

        lblContact = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, 150, 30)];
        [lblContact setFont:[UIFont fontWithName:@"HelveticaNeue-Medium" size:13]];
        [lblContact setTag:13];
        [lblContact setTextColor:[self.delegate colorFromHexString:@"#fffbff"]];
        [lblContact setBackgroundColor:[UIColor clearColor]];

        /* Received Message */

        lblMessage = [[UILabel alloc] initWithFrame:CGRectMake(10, 20, 280, 50)];
        [lblMessage setNumberOfLines:0];
        [lblMessage setLineBreakMode:NSLineBreakByWordWrapping];
        [lblMessage setBackgroundColor:[UIColor clearColor]];
        [lblMessage setFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:12]];
        [lblMessage setTextColor:[self.delegate colorFromHexString:@"#fffbff"]];
        [lblMessage setTag:14];


        /* Date received */

        lblDate = [[UILabel alloc] initWithFrame:CGRectMake(10, 65, 65, 30)];
        [lblDate setText:@"4 hours ago"];
        [lblDate setFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:11]];
        [lblDate setTextColor:[self.delegate colorFromHexString:@"#fffbff"]];
        [lblDate setTag:15];

        /* Subview Logic */

        [wrapper addSubview:lblContact];
        [wrapper addSubview:lblMessage];
        [wrapper addSubview:lblDate];

        [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
        [cell.contentView addSubview:wrapper];
    
    else
    
        lblContact = (UILabel *)[cell.contentView viewWithTag:13];
        lblMessage = (UILabel *)[cell.contentView viewWithTag:14];
        wrapper = [cell.contentView viewWithTag:19];
    

    MessageObject * model = [self.chats objectAtIndex:indexPath.row];

    CGFloat height = [self heightForMessage:indexPath];

    [lblMessage setFrame:CGRectMake(10, 25, 280, height)];
    [lblMessage setText:model.message];

    [lblContact setText:model.clientModel.firstName];

    if(height < 50)
    
        NSLog(@"wrapper size : %d for row %d ", 90, indexPath.row);
        [wrapper setFrame:CGRectMake(10, 5, 300, 90)];
    
    else
    
        NSLog(@"wrapper size : %f for row %d ", height + 60, indexPath.row);

        [wrapper setFrame:CGRectMake(10, 5, 300, height + 60)];
        [lblDate setFrame:CGRectMake(10, height + 20, 65, 30)];
    

    [cell.contentView setBackgroundColor:[UIColor greenColor]];
    [cell setBackgroundColor:[UIColor clearColor]];

    return cell;
 

【问题讨论】:

您的方法没问题,但您必须模拟单元格的布局才能获得实际大小。它显示标签和包装,您必须单独获取它们的尺寸 【参考方案1】:

考虑显着改进您的代码...因为它现在几乎无法阅读 1)摆脱硬代码,例如20, 280. 改为考虑使用常量 (CONTACT_LABEL_TO_MESSAGE_LABEL_OFFSET, MESSAGE_LABEL_WIDTH) 2)摆脱向 UITableViewCell 添加自定义子视图并用硬编码(参见 1)数字标记它们 - 而是子类 UITableViewCell,在 .xib 或 .storyboard 中对其进行原型设计,为您的类制作方便的方法 - 例如 setDate:、setMessage:、setContactName: .所有帧都应在您的子类 -layoutSubviews 方法中计算。

乍一看: 1)你将280的宽度设置为lblMessage,当你将它的文本限制为290的宽度时,它会导致错误的高度计算

【讨论】:

我会在初稿开始工作时更新我的​​代码,我以编程方式做所有事情,所以没有 xib 的故事板或其他任何东西 嗯,从项目开始的良好编码风格可以节省时间......不过,在调试器中逐步检查您的帧。数字有问题【参考方案2】:

其中一个错误是当您为新单元格创建包装视图时:

[wrapper viewWithTag:19];

应该是:

[wrapper setTag:19];

当您尝试在重复使用的单元格上调整 wrapper 的大小时,这可能会导致其为 nil。

【讨论】:

现在我对 lblDate 有时会消失有一点问题.. 有什么想法吗? 这可能是由于重复使用单元格所致。如果高度相当大,那么单元格会被重新用于较小的消息,lblDate 标签的框架不会被重置到原来的位置。【参考方案3】:

我建议使用原型单元并通过使用给定索引路径的数据配置原型来计算单元高度。只需将一个单元格出列(是的,您可以在cellForRowAtIndexPath 之外执行此操作)并按住它进行高度计算。对于动态高度标签,您通常会在单元格的配置逻辑中调用[label sizeToFit]。 this question 中对此技术有更详细的讨论。

如果你想看看第 3 方库,我的 TLIndexPathTools 库提供了一个基本表视图类 TLTableViewController,它内置了对使用原型的动态行高的支持:

    对于静态的、非数据驱动的高度,它将返回原型的默认高度。因此,如果您的原型来自 Storyboard,它将遵循 Storyboard 自定义单元格高度。 对于动态的、数据驱动的高度,任何实现TLDynamicHeightView 协议的自定义单元格都将自动计算动态高度。

【讨论】:

【参考方案4】:

我会告诉你我用来解决这个问题的方法。

使用array 存储高度(使用sizeWithFont)。它现在已被弃用,因此您应该尝试改用 sizeWithAttributes。 记录array 的高度和实际标签的高度,看看它们是否匹配。 确保使用的UIFontsizeWithFont 和yourLabel.font 中的相同。 检查您是否在单元格中进行了任何类型的填充,例如在单元格顶部留下像素。

这些是一般提示。

【讨论】:

【参考方案5】:

当您在tableView: cellForRowAtIndexPath: 中创建单元格时,您可以将其存储到NSMutableArray 然后当tableView: heightForRowAtIndexPath: 简单地检索单元格并指定它的高度。

我大部分时间都是这样做的,但是如果您使用 大量 单元格,它可能会占用大量内存。 或者也许你只能将单元格的大小存储在 NSMutableArray 中。

编辑:

我不确定我是否理解正确。 您希望您的单元格能够包含您的包装器吗?

cell.contentView.frame = CGRectMake(0, 0, cell.contentView.frame.size.width, wrapper.frame.origin.y + wrapper.frame.size.height);

将单元格存储在tableView: cellForRowAtIndexPath: 的NSMutableArray 中,根据tableView: heightForRowAtIndexPath: 中的索引检索它

【讨论】:

这适用于少数细胞——虽然它是鹰派。更好的是拥有一个原型单元,您可以一次又一次地进行测量,然后在 cellForRow 中初始化/重用正确的单元 @Daij-Djan 非常聪明【参考方案6】:

要计算标签高度,您应该考虑不同的因素,您必须通过牢记文本长度及其字体以及要添加的框架边界来计算大小。下面是如何计算动态文本大小的示例。

CGSize boundingSize = CGSizeMake(frame.size.width, CGFLOAT_MAX);
    CGSize requiredSize = [text sizeWithFont:yourFont constrainedToSize:boundingSize lineBreakMode:NSLineBreakByWordWrapping];

【讨论】:

以上是关于UITableViewCell 的动态高度和里面的内容的主要内容,如果未能解决你的问题,请参考以下文章

具有动态内容、动态布局和动态高度的 UITableViewCell

动态高度 UITableViewCell 闪烁

NSLayoutConstraint 和动态 UITableViewCell 高度

根据单元格高度在 UITableViewCell 内动态设置 UIImageView 高度

约束 UIImageView 高度以尊重 UITableViewCell 中的动态 UILabel 高度

具有动态 UILabel 和内容压缩/阻力的 UITableViewCell 高度不正确