I've tried plenty of answers on SO but nothing really worked. I'm probably doing something wrong so I need someone to point out what I'm doing wrong..
- (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;
}
I get "NSInvalidArgumentException" with reason "NSConcreteAttributedString initWithString:: nil value" because self.cell.comment.text isn't getting anything by the time I set cell height but it does come through just not when heightForRowAtIndexPath gets called.
Many people have commented on that answer that this method works just fine so I guess I'm missing something?
Edit
I'm setting self.cell.comment.text here -
- (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;}
Your problem as you already comment is that function heightForRowAtIndexPath
is called before cells are populated.
For every cell that become visible it first call
So you know that
a) your text is not yet populated on the cell
b) some other text might be inside, because apple use reusable cells, so UITableView can grab some cell (with different text) and try to resize it and then populate it.
In your case it will grab some other text, resize cell to its size and then populate it with some other text that is (probably) different size then previous text.
But inside cell populations you set text from some business logic (maybe array?), and you can get same text in this method.
if cell population you call
cell.comment.text = [self.someArray getObjectAtIndex:index.row];
you call this in your heightForRowAtIndexPath
method.
NSString *text = [self.someArray getObjectAtIndex:index.row];
I see your edit just call :
- (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;
}
ADDITION :
Tel say you would like to have cells like this :
-------------------------
| bla bla bla |
------------------------
| second longer text |
| over more line |
------------------------
You need to have texts bla bla
and "second longer text over more line" somewhere saved.
Let say you have array with size 2.
NSArray * myTextArray = @[@"bla bla", @"second longer text over more line"];
and when populating cells
- (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;
}
because heightForRowAtIndexPath
is called before cellForRowAtIndexPath
we need to check text from business (array) side and not visual.
- (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;
}
EXAMPLE :
#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;
}
this example works.