设置自定义UITableViewCells的高度。

232

我正在使用自定义的UITableViewCell,其中包含要显示的标签、按钮和图像视图。在该单元格中有一个标签,其文本是一个NSString对象,字符串的长度可能是可变的。因此,我无法在UITableViewheightForCellAtIndex方法中设置一个固定的单元格高度。单元格的高度取决于标签的高度,可以使用NSStringsizeWithFont方法确定标签的高度。我尝试过使用它,但似乎出了些问题。如何解决?

下面是用于初始化单元格的代码。

if (self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier])
{
    self.selectionStyle = UITableViewCellSelectionStyleNone;
    UIImage *image = [UIImage imageNamed:@"dot.png"];
    imageView = [[UIImageView alloc] initWithImage:image];
    imageView.frame = CGRectMake(45.0,10.0,10,10);

    headingTxt = [[UILabel alloc] initWithFrame:   CGRectMake(60.0,0.0,150.0,post_hdg_ht)];
    [headingTxt setContentMode: UIViewContentModeCenter];
    headingTxt.text = postData.user_f_name;
    headingTxt.font = [UIFont boldSystemFontOfSize:13];
    headingTxt.textAlignment = UITextAlignmentLeft;
    headingTxt.textColor = [UIColor blackColor];

    dateTxt = [[UILabel alloc] initWithFrame:CGRectMake(55.0,23.0,150.0,post_date_ht)];
    dateTxt.text = postData.created_dtm;
    dateTxt.font = [UIFont italicSystemFontOfSize:11];
    dateTxt.textAlignment = UITextAlignmentLeft;
    dateTxt.textColor = [UIColor grayColor];

    NSString * text1 = postData.post_body;
    NSLog(@"text length = %d",[text1 length]);
    CGRect bounds = [UIScreen mainScreen].bounds;
    CGFloat tableViewWidth;
    CGFloat width = 0;
    tableViewWidth = bounds.size.width/2;
    width = tableViewWidth - 40; //fudge factor
    //CGSize textSize = {width, 20000.0f}; //width and height of text area
    CGSize textSize = {245.0, 20000.0f}; //width and height of text area
    CGSize size1 = [text1 sizeWithFont:[UIFont systemFontOfSize:11.0f]
                        constrainedToSize:textSize lineBreakMode:UILineBreakModeWordWrap];

    CGFloat ht = MAX(size1.height, 28);
    textView = [[UILabel alloc] initWithFrame:CGRectMake(55.0,42.0,245.0,ht)];
    textView.text = postData.post_body;
    textView.font = [UIFont systemFontOfSize:11];
    textView.textAlignment = UITextAlignmentLeft;
    textView.textColor = [UIColor blackColor];
    textView.lineBreakMode = UILineBreakModeWordWrap;
    textView.numberOfLines = 3;
    textView.autoresizesSubviews = YES;

    [self.contentView addSubview:imageView];
    [self.contentView addSubview:textView];
    [self.contentView addSubview:webView];
    [self.contentView addSubview:dateTxt];
    [self.contentView addSubview:headingTxt];
    [self.contentView sizeToFit];

    [imageView release];
    [textView release];
    [webView release];
    [dateTxt release];
    [headingTxt release];
}

这是高度和宽度出现问题的标签:

textView = [[UILabel alloc] initWithFrame:CGRectMake(55.0,42.0,245.0,ht)];
9个回答

508

你的 UITableViewDelegate 应该实现 tableView:heightForRowAtIndexPath: 方法。

Objective-C

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return [indexPath row] * 20;
}

Swift 5

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return indexPath.row * 20
}

你可能想要使用NSStringsizeWithFont:constrainedToSize:lineBreakMode:方法来计算行高,而不是简单地在indexPath上执行一些愚蠢的数学运算 :)


1
你的意思不是 [indexPath row] + 20 吗? - fulvio
12
Fulvio的意思是要让各行高度显然不同。 - rpetrich
5
设置为特定高度(例如44像素):返回44。 - mpemburn
你能提供一些关于如何使用 sizeWithFont:constrainedToSize:lineBreakMode: 的更多细节吗?我在我的单元格中有一个 textView,并且我想在 heightForRowAtIndexPath: 中获取它的高度,以便设置行的高度,但我不知道该如何获取它!谢谢。 - rdurand
如果你返回"[index row] * (some-value)",那么第一个单元格可能不可见,因为它返回的第一个值是零。因此最好计算所需的单元格高度并在此返回计算值,以使所有单元格可见。 - Vinayak Hejib

135
如果所有的行高度相同,只需要设置UITableView的rowHeight属性,而不是实现heightForRowAtIndexPath方法。苹果文档指出:“使用tableView:heightForRowAtIndexPath:而非rowHeight会对性能产生影响。每次显示表格视图时,它都会调用委托对象的tableView:heightForRowAtIndexPath:方法来获取每一行的高度信息,当表格视图中行数较多(大约1000行或更多)时,这可能导致严重的性能问题。”

5
当所有行的高度相同时,这个方法可以完美地工作。但是,原帖的情况并非如此,因此答案本身并不是不正确的,但也不能回答原始问题。然而,我认为需要注意的是,设置rowHeight很可能比覆盖heightForRowAtIndexPath会产生更好的性能,因为像计算滚动条尺寸之类的事情在这种情况下是O(1)而不是O(n)。 - Daniel Albuschat
2
请注意(出于显而易见的原因),如果设置了tableView.rowHeight,则不会调用heightForRowAtIndexPath方法,以防您像我一样想弄清楚为什么。 - devios1

25
在自定义的UITableViewCell控制器中添加以下内容。
-(void)layoutSubviews {  

    CGRect newCellSubViewsFrame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
    CGRect newCellViewFrame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height);

    self.contentView.frame = self.contentView.bounds = self.backgroundView.frame = self.accessoryView.frame = newCellSubViewsFrame;
    self.frame = newCellViewFrame;

    [super layoutSubviews];
}

在UITableView控制器中添加以下内容

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return [indexPath row] * 1.5; // your dynamic height...
}

CGRect newCellSubViewsFrame = self.bounds;CGRect newCellViewFrame = self.frame; - Pizzaiola Gorgonzola

17
#define FONT_SIZE 14.0f
#define CELL_CONTENT_WIDTH 300.0f
#define CELL_CONTENT_MARGIN 10.0f

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath      *)indexPath;
{
   /// Here you can set also height according to your section and row
   if(indexPath.section==0 && indexPath.row==0)
   {
     text=@"pass here your dynamic data";

     CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);

     CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE]      constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];

     CGFloat height = MAX(size.height, 44.0f);

     return height + (CELL_CONTENT_MARGIN * 2);
   }
   else
   {
      return 44;
   }
}

- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell;
    UILabel *label = nil;

    cell = [tv dequeueReusableCellWithIdentifier:@"Cell"];
    if (cell == nil)
    {
       cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"Cell"];
    }
    ********Here you can set also height according to your section and row*********
    if(indexPath.section==0 && indexPath.row==0)
    {
        label = [[UILabel alloc] initWithFrame:CGRectZero];
        [label setLineBreakMode:UILineBreakModeWordWrap];
        [label setMinimumFontSize:FONT_SIZE];
        [label setNumberOfLines:0];
        label.backgroundColor=[UIColor clearColor];
        [label setFont:[UIFont systemFontOfSize:FONT_SIZE]];
        [label setTag:1];

        // NSString *text1 =[NSString stringWithFormat:@"%@",text];

        CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);

        CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];

        if (!label)
        label = (UILabel*)[cell viewWithTag:1];


        label.text=[NSString stringWithFormat:@"%@",text];
        [label setFrame:CGRectMake(CELL_CONTENT_MARGIN, CELL_CONTENT_MARGIN, CELL_CONTENT_WIDTH          - (CELL_CONTENT_MARGIN * 2), MAX(size.height, 44.0f))];
        [cell.contentView addSubview:label];
    }
return cell;
}

9

我看到了很多解决方案,但都是错误或不完整的。 您可以在viewDidLoad和自动布局中使用5行代码解决所有问题。 这适用于Objective-C:

_tableView.delegate = self;
_tableView.dataSource = self;
self.tableView.estimatedRowHeight = 80;//the estimatedRowHeight but if is more this autoincremented with autolayout
self.tableView.rowHeight = UITableViewAutomaticDimension;
[self.tableView setNeedsLayout];
[self.tableView layoutIfNeeded];
self.tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0) ;

对于 Swift 2.0:

 self.tableView.estimatedRowHeight = 80
 self.tableView.rowHeight = UITableViewAutomaticDimension      
 self.tableView.setNeedsLayout()
 self.tableView.layoutIfNeeded()
 self.tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0)

现在使用xib创建单元格或在Storyboard中将其放入tableview中,这样您就不需要实现任何其他内容或重写。 (不要忘记行数为0)和底部标签(约束)将“内容抱紧优先级 - 垂直”降级为250。

enter image description here enter image description here

您可以在以下网址中下载代码: https://github.com/jposes22/exampleTableCellCustomHeight


1
两天后,我将使用相同的单元格放置更多具有字符ImageView和其他subtableview的图像,此方法始终有效。非常感谢您的+1。 - Jose Pose S
太棒了!! 这也将帮助到其他人。谢谢 :) - Jagdish
如果您是通过编程方式创建UITableViewCell而不是使用Interface Builder,则此解决方案无法正常工作。 - MB_iOSDeveloper
这个解决方案没有限制和优先级是行不通的。如果你想按程序构建,你也应该以编程方式设置限制并改变优先级。如果您需要,我可以上传一个示例。 - Jose Pose S
@SimonMoshenko 是的,这个解决方案可以处理异步数据更新,但是如果你改变了所有文本和数据,可能需要重新加载数据。如果你使用MVC,请从单元格调用控制器的委托并重新加载数据;如果你使用MVVM,那么你不会有问题,因为当数据到来时,你的视图可以重新加载数据。另一方面,我使用SDWebImage这段代码,我不需要重新加载数据。 - Jose Pose S
显示剩余2条评论

8
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
    CGSize constraintSize = {245.0, 20000}
    CGSize neededSize = [ yourText sizeWithFont:[UIfont systemFontOfSize:14.0f] constrainedToSize:constraintSize  lineBreakMode:UILineBreakModeCharacterWrap]
if ( neededSize.height <= 18) 

   return 45
else return neededSize.height + 45 
//18 is the size of your text with the requested font (systemFontOfSize 14). if you change fonts you have a different number to use  
// 45 is what is required to have a nice cell as the neededSize.height is the "text"'s height only
//not the cell.

}

6
为了自动设置行高和预估行高,请按以下步骤进行操作,确保单元格/行高的布局生效。
  • 分配并实现tableView的dataSource和delegate
  • UITableViewAutomaticDimension分配给rowHeight和estimatedRowHeight
  • 实现delegate/dataSource方法(如heightForRowAt),并返回值UITableViewAutomaticDimension

-

Objective-C:

// in ViewController.h
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>

  @property IBOutlet UITableView * table;

@end

// in ViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    self.table.dataSource = self;
    self.table.delegate = self;

    self.table.rowHeight = UITableViewAutomaticDimension;
    self.table.estimatedRowHeight = UITableViewAutomaticDimension;
}

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

    return UITableViewAutomaticDimension;
}

Swift:

@IBOutlet weak var table: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()

    // Don't forget to set dataSource and delegate for table
    table.dataSource = self
    table.delegate = self

    // Set automatic dimensions for row height
    table.rowHeight = UITableViewAutomaticDimension
    table.estimatedRowHeight = UITableViewAutomaticDimension
}



// UITableViewAutomaticDimension calculates height of label contents/text
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableViewAutomaticDimension
}

对于UITableviewCell中的标签实例

  • 将行数设置为0(并且换行模式为truncate tail)
  • 将所有约束(顶部,底部,左右)相对于其父视图/单元格容器设置。
  • 可选:如果您想要最小垂直区域被标签覆盖,即使没有数据,请设置标签的最小高度。

enter image description here

注意:如果您有多个具有动态长度的标签(UIElements),应根据其内容大小进行调整:为要扩展/压缩的标签调整“内容紧凑性和压缩阻力优先级”。

在此示例中,我将紧凑性低和压缩阻力优先级高设置为更重要的第二个(黄色)标签的内容。

enter image description here


5
感谢所有涉及此主题的帖子,有一些非常有用的方法可以调整UITableViewCell的行高。
以下是其他人提出的一些概念的汇编,在构建iPhone和iPad应用程序时非常有帮助。您还可以访问不同的部分,并根据视图的不同大小进行调整。
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
    int cellHeight = 0;

    if ([indexPath section] == 0) 
    {
        cellHeight = 16;
        settingsTable.rowHeight = cellHeight;
    }
    else if ([indexPath section] == 1)
    {
        cellHeight = 20;
        settingsTable.rowHeight = cellHeight;
    }

    return cellHeight;
}
else
{
    int cellHeight = 0;

    if ([indexPath section] == 0) 
    {
        cellHeight = 24;
        settingsTable.rowHeight = cellHeight;
    }
    else if ([indexPath section] == 1)
    {
        cellHeight = 40;
        settingsTable.rowHeight = cellHeight;
    }

    return cellHeight;
}
return 0;
} 

在XCode中进行分析时,行"int cellHeight;"会创建一个逻辑错误,即"Undefined or garbage value returned to caller."。将cellHeight设置为0可以修复此错误。如果在类似于上面的if/else if语句中,未将NSString设置为nil,则也会出现此错误。 - whyoz

3
为了实现标签文本增加时的动态单元格高度,您首先需要计算在-heightForRowAtIndexPath委托方法中要使用的文本高度,并将其与其他标签、图像的高度相加(文本最大高度+其他静态组件的高度),并在单元格创建时使用相同的高度返回。
#define FONT_SIZE 14.0f
#define CELL_CONTENT_WIDTH 300.0f  
#define CELL_CONTENT_MARGIN 10.0f

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

    if (indexPath.row == 2) {  // the cell you want to be dynamic

        NSString *text = dynamic text for your label;

        CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);
        CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];

        CGFloat height = MAX(size.height, 44.0f);

        return height + (CELL_CONTENT_MARGIN * 2);
    }
    else {
        return 44; // return normal cell height
    }
}

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

    static NSString *CellIdentifier = @"Cell";

    UILabel *label;

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] ;
    }

    label = [[UILabel alloc] initWithFrame:CGRectMake(10, 5, 280, 34)];

    [label setNumberOfLines:2];

    label.backgroundColor = [UIColor clearColor];

    [label setFont:[UIFont systemFontOfSize:FONT_SIZE]];

    label.adjustsFontSizeToFitWidth = NO;

    [[cell contentView] addSubview:label];


    NSString *text = dynamic text fro your label;

    [label setText:text];

    if (indexPath.row == 2) {// the cell which needs to be dynamic 

        [label setNumberOfLines:0];

        CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);

        CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];

        [label setFrame:CGRectMake(CELL_CONTENT_MARGIN, CELL_CONTENT_MARGIN, CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), MAX(size.height, 44.0f))];

    }
    return  cell;
}

@Tisho 你测试过点踩功能了吗 Tisho 兄弟。小心测试,它可行兄弟!!!! - Samir Jwarchan
1
我没有给你的答案投反对票,只是改善了格式。 - Tisho

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接