以编程方式动态更改单元格高度
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;
}
因为 heightForRowAtIndexPath
在 cellForRowAtIndexPath
之前调用,我们需要从业务(数组)端而不是视觉检查文本。
- (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);}
这绝对有效。
我已经尝试了很多关于 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;
}
因为 heightForRowAtIndexPath
在 cellForRowAtIndexPath
之前调用,我们需要从业务(数组)端而不是视觉检查文本。
- (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);}
这绝对有效。