以编程方式动态更改单元格高度

Dynamically change cell height programmatically

我已经尝试了很多关于 SO 的答案,但没有任何效果。我可能做错了什么所以我需要有人指出我做错了什么..

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

    NSLog(@"text : %@", self.cell.comment.text);

    NSString *text = self.cell.comment.text;
    CGFloat width = self.cell.frame.size.width;
    UIFont *font = [UIFont fontWithName:@"HelveticaNeue" size:15];
    NSAttributedString *attributedText =
    [[NSAttributedString alloc] initWithString:text
                                    attributes:@{NSFontAttributeName: font}];
    CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
                                               options:NSStringDrawingUsesLineFragmentOrigin
                                               context:nil];
    CGSize size = rect.size;
    CGFloat height = ceilf(size.height);

    return height;
}

我得到 "NSInvalidArgumentException" 的原因是 "NSConcreteAttributedString initWithString:: nil value" 因为 self.cell.comment.text 在我设置单元格高度时没有得到任何东西,但它确实只是在调用 heightForRowAtIndexPath 时才通过。

很多人评论说这个方法工作得很好所以我想我错过了什么?

编辑

我在这里设置 self.cell.comment.text -

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object{
static NSString *simpleTableIdentifier = @"cell";

self.cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (self.cell == nil) {
    self.cell = [[CommentCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}

// Configure the cell

self.cell.comment.text = [object objectForKey:@"comment"];
[self.cell.comment sizeToFit];

return self.cell;}

正如您已经评论的那样,您的问题是函数 heightForRowAtIndexPath 在填充单元格之前被调用。

对于每个可见的单元格,它首先调用

  • 获取此单元格的高度
  • 填充单元格

所以你知道

a) 您的文本尚未填充到单元格中

b) 一些其他文本可能在里面,因为苹果使用可重复使用的单元格,所以 UITableView 可以抓取一些单元格(具有不同的文本)并尝试调整它的大小,然后填充它。

在你的情况下,它会抓取一些其他文本,将单元格调整到它的大小,然后用一些其他文本填充它(可能)与以前的文本大小不同。

但是在单元格群体中,您可以从一些业务逻辑(也许是数组?)中设置文本,并且您可以在此方法中获得相同的文本。

如果您调用细胞群

cell.comment.text = [self.someArray getObjectAtIndex:index.row];

您在 heightForRowAtIndexPath 方法中调用它。

NSString *text = [self.someArray getObjectAtIndex:index.row];

我看到你的编辑只是调用:

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

NSLog(@"text : %@", self.cell.comment.text);

NSString *text = [object objectForKey:@"comment"];;
CGFloat width = self.cell.frame.size.width;
UIFont *font = [UIFont fontWithName:@"HelveticaNeue" size:15];
NSAttributedString *attributedText =
[[NSAttributedString alloc] initWithString:text
                                attributes:@{NSFontAttributeName: font}];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
                                           options:NSStringDrawingUsesLineFragmentOrigin
                                           context:nil];
CGSize size = rect.size;
CGFloat height = ceilf(size.height);

return height;
}

添加:

电话说你想要这样的单元格:

-------------------------
|  bla bla bla         |
------------------------
| second longer text   |
| over more line       |
------------------------

您需要将文本 bla bla 和 "second longer text over more line" 保存在某处。

假设您有大小为 2 的数组。

NSArray * myTextArray = @[@"bla bla", @"second longer text over more line"];

填充单元格时

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object{
   static NSString *simpleTableIdentifier = @"cell";

   self.cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
   if (self.cell == nil) {
        self.cell = [[CommentCell alloc] initWithStyle:UITableViewCellStyleDefault         reuseIdentifier:simpleTableIdentifier];
    }

    // Configure the cell

    self.cell.comment.text = [myTextArray objectAtIndex:indexPath.row];
    [self.cell.comment sizeToFit];

    return self.cell;
 }

因为 heightForRowAtIndexPathcellForRowAtIndexPath 之前调用,我们需要从业务(数组)端而不是视觉检查文本。

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

   NSLog(@"text : %@", self.cell.comment.text); // -> this is null because cell is not populated yet.

   NSString *text = [myTextArray objectAtIndex:indexPath.row]; -> this is same text as we will take when populating cell and is not random.
   CGFloat width = self.cell.frame.size.width;
   UIFont *font = [UIFont fontWithName:@"HelveticaNeue" size:15];
   NSAttributedString *attributedText =
   [[NSAttributedString alloc] initWithString:text
                                attributes:@{NSFontAttributeName: font}];
   CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
                                           options:NSStringDrawingUsesLineFragmentOrigin
                                           context:nil];
   CGSize size = rect.size;
   CGFloat height = ceilf(size.height);

   return height;
}

示例:

#import "ViewController.h"

@interface ViewController ()

@property NSArray * myArray;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.myArray = @[@"Some short text",@"Some longer text that take some more space throw more lines",@"bfusdbfjdsfjs fj yfsdy fgsydu fyudsfy fyudsyu fdsy fuysdyuf ydsug fyu sdgyfgsuyff ius fhs fiusdhi ufdshu uifsd ufsdh hfiuds uifdsh fsduih ufdshu hfsd ifshui"];

}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 3;
}


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

    NSString *text = self.myArray[indexPath.row];
    CGFloat width = 300;
    UIFont *font = [UIFont fontWithName:@"HelveticaNeue" size:15];
    NSAttributedString *attributedText =
    [[NSAttributedString alloc] initWithString:text
                                attributes:@{NSFontAttributeName: font}];
    CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
                                           options:NSStringDrawingUsesLineFragmentOrigin
                                           context:nil];
    CGSize size = rect.size;
    CGFloat height = ceilf(size.height);

    return height;
}

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

    static NSString *simpleTableIdentifier = @"cell";

    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
    }

    // Configure the cell

    cell.textLabel.text = self.myArray[indexPath.row];
    cell.textLabel.numberOfLines = 0;
    return cell;
}

这个例子有效。

在 heightForRowAtIndexPath:

对于宽度,我想你可以解决它,不需要这一行:

 CGFloat width = self.cell.frame.size.width;

对于评论文字

NSString *text = self.cell.comment.text;

试试这个

NSString *text = [object objectForKey:@"comment"];

花了一整天的时间想办法解决这个问题,最后发现解决方案非常简单,我只需要知道如何更好地使用 Parse。

感谢 Marko,我了解了 UITableView 的真正工作原理,这很棒,但我的问题的解决方案有点不同。

正如大家所建议的那样,我的问题是假设 heightForRowAtIndexPath 会在填充所有单元格后被调用,但我不需要数组来保存对象或进行任何更改。 Parse 显然会在调用 heightForRowAtIndexPath 之前保存所有检索到的对象,它们都在 self.objects.

self.cell.comment.text = [self.objects objectAtIndex:indexPath.row];

正如安迪热情地建议的那样,我现在正在使用自动调整大小的 UITableViewCell。

在你的 PFQueryTableViewController

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

PFObject *object = [self.objects objectAtIndex:indexPath.row];

if (object) {
    NSString *commentString = [self.objects[indexPath.row] objectForKey:@"comment"];

    NSLog(@"commentString : %@",commentString);

    CommentCell *cell = [[CommentCell alloc] init];
    cell.textLabel.text = commentString;


    [cell setNeedsLayout];
    [cell layoutIfNeeded];

    self.height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

    self.height += 1;

}

return self.height;}

在您的自定义单元格中

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {

    self.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
    self.textLabel.numberOfLines = 0;
    self.textLabel.translatesAutoresizingMaskIntoConstraints = NO;


    [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-6-[bodyLabel]-6-|" options:0 metrics:nil views:@{ @"bodyLabel": self.textLabel }]];
    [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-6-[bodyLabel]-6-|" options:0 metrics:nil views:@{ @"bodyLabel": self.textLabel }]];

}

return self;}


 - (void)layoutSubviews{
[super layoutSubviews];

[self.contentView setNeedsLayout];
[self.contentView layoutIfNeeded];

self.textLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.textLabel.frame);}

这绝对有效。