iOS 计算内容范围(例如,动态单元格高度)

示例

想要计算标签将占用的帧的常见用例是适当调整表格视图单元的大小。推荐的方法是使用NSString方法boundingRectWithSize:options:attributes:context:。

options 采用String绘图选项:

  • NSStringDrawingUsesLineFragmentOrigin 应该用于多行标签

  • NSStringDrawingTruncatesLastVisibleLine|如果最大行数,则应使用运算符添加

attributes是NSDictionary影响属性字符串的属性(完整列表:Apple Docs),但是影响高度的因素包括:

  • NSFontAttributeName:非常重要,大小和字体系列是标签显示大小的关键部分。

  • NSParagraphStyleAttributeName:用于自定义文本的显示方式。这包括行距,文本对齐,截断样式和其他一些选项。如果您没有明确更改任何这些值,则不必担心太多,但是如果您在IB上切换了一些值,则可能很重要。

context应该是nil因为主要NSStringDrawingContext用例是允许字体调整大小以适应指定的矩形,如果我们要计算动态高度,则不应该这样。

目标C

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

    NSString *labelContent = cell.theLabel.text;
    // 如果您对字体进行了最少的自定义或对一些值进行硬编码感到满意,则可以选择直接从数据源获取内容。
//    NSString * labelContent = [self.dataSource objectAtIndexPath:indexPath];
    
    // 如果从数据源中检索到值,则可能会进行硬编码
    NSFont *labelFont = [cell.theLabel font];
    
    // NSParagraphStyle,即使您未编写任何更改,这些值也可能已在IB中更改
    NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
   paragraphStyle.lineBreakMode= NSLineBreakByWordWrapping; 
   paragraphStyle.alignment= NSTextAlignmentCenter;

    NSDictionary *attributes = @{NSFontAttributeName: labelFont,
                                 NSParagraphStyleAttributeName: paragraphStyle};

    // 宽度对高度也很重要
    CGFloat labelWidth = CGRectGetWidth(cell.theLabel.frame);
    // 如果您到目前为止一直在进行硬编码,则可以通过从tableView.bounds.size.width中减去左右两侧的填充来获得此值
//    CGFloat labelWidth = CGRectGetWidth(tableView.frame)-20.0f-20.0f;

    CGRect bodyBounds = [labelContent boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];

    return CGRectGetHeight(bodyBounds) + heightOfObjectsOnTopOfLabel + heightOfObjectBelowLabel;
}

Swfit 3

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    var cell = tableView.cellForRow(atIndexPath: indexPath)!
    var labelContent = cell.theLabel.text
    var labelFont = cell.theLabel.font
    var paragraphStyle = NSMutableParagraphStyle()

   paragraphStyle.lineBreakMode= .byWordWrapping
   paragraphStyle.alignment= .center

    var attributes = [NSFontAttributeName: labelFont, NSParagraphStyleAttributeName: paragraphStyle]

    var labelWidth: CGFloat = cell.theLabel.frame.width

    var bodyBounds = labelContent.boundingRect(withSize: CGSize(width: width, height: CGFLOAT_MAX), options: .usesLineFragmentOrigin, attributes: attributes, context: nil)

    returnbodyBounds.height+ heightOfObjectsOnTopOfLabel + heightOfObjectBelowLabel
}

相反,如果您有设定的最大行数,则首先需要计算单行的高度,以确保我们得到的值不超过允许的大小:

    // 我们通过省略NSStringDrawingUsesLineFragmentOrigin选项来计算线的高度,该选项将假定一个无限宽的标签
    CGRect singleLineRect = [labelContent boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)
                                                 options:NSStringDrawingTruncatesLastVisibleLine
                                                 context:nil];
    CGFloat lineHeight = CGRectGetHeight(singleLineRect);
    CGFloat maxHeight = lineHeight * cell.theLabel.numberOfLines;

    // 现在您可以适当地调用该方法
    CGRect bodyBounds = [labelContent boundingRectWithSize:CGSizeMake(width, maxHeight) options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingTruncatesLastVisibleLine) attributes:attributes context:nil];

    return CGRectGetHeight(bodyBounds) + heightOfObjectsOnTopOfLabel + heightOfObjectBelowLabel;