How do i handle dynamic text height in a cell? [duplicate] - ios

This question already has answers here:
UITableViewCell, UITextView with dynamic height
(3 answers)
Closed 8 years ago.
I have a UILabel inside a UITableViewCell. I am not using autolayout, but creating and adding the views manually.
The UILabel can contain a variable amount of text so in my tableview delegate i implement heightForRowAtIndexPath.
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
TableCellWithDocument *cell = (TableCellWithDocument*)[self tableView: tableView cellForRowAtIndexPath: indexPath];
NSLog(#"Cell frame: %#", NSStringFromCGRect(cell.frame));
return [cell heightForCellForWidth: cell.frame.size.width];
The problem i'm having is that when this delegate method is called, the frame size of the cell is not correct. It just returns "default" 320x44.
So when i try to calculate the label size, i have the wrong cell width and my cell height gets the wrong height.
In my subclassed TableCellWithDocument class, i can override layoutSubviews and get the correct cell width, but that is too late since heightForRowAtIndexPath has already been called.
How should i handle this?

You are doing it in recursive manner. If you carefully debug your code, you will find that heightForRowAtIndexPath is called first to estimate cell height, and cellForRowAtIndexPath is called later.
Ideally, within heightForRowAtIndexPath, you should take all your data source content, and estimate the size of it. cellForRowAtIndexPath should be able to use this height to set the cell content.
For example, if there is some text to be displayed, you could use [UILabel sizeThatFits] or [NSString sizeWithFont] method to estimate cell.textLabel size, and so on.
Remember that no answer is the right answer and you must do tweaks according your own data - be it strings, images or anything else. It's data size, not the size of your controls - that must decide what you return from heightForRowAtIndexPath.

Try doing this in Your heightForRowAtIndexPath
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGSize size = [text sizeWithFont:[UIFont fontWithName: #"YourFont" size:24.0];
constrainedToSize:CGSizeMake(kMessageTextWidth, CGFLOAT_MAX)
lineBreakMode:NSLineBreakByWordWrapping];
return MAX(size.height + 17.0f);
}

Related

Increasing Tablview Cell Height and Print the data at specific row

I have one UItableview and I have one customCell for printing the data in the UITable.
Now, I want to put more data in specific cell. When I select on cell, it should increase the height of cell and put that data. How is it possible.?
In DidSelect make the height of particular selected cell to your required cell.
And reload the table.
This is helpful : How to programmatically increase UITableView cell's height in iPhone?
Call reload table data by changing the parameters of row and to find the height of a cell after adding data use:
CGSize size=[youstring sizeWithFont:yourlabel.font constrainedToSize:labelfontsize lineBreakMode:lineBreakmodestyle];
And get height from size.height and return the height in heightForRowAtIndexPath method while reloading tableview.
what you should do is First Calculate the Size of your content for which you have to increase the cell height
//Suppose here is your string which you want to show
-(float) calculateHeight:(NSString *)dataStr
{
CGSize size = [dataStr sizeWithFont:[UIFont fontWithName:#"Helvetica" size:17] constrainedToSize:CGSizeMake(280, 999) lineBreakMode:UILineBreakModeWordWrap];
NSLog(#"%f",size.height);
return size.height + 10;
}
This Above function will return the height according to your Text Size.
Now just have to call this function on the
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
}
delegete and reload the tableview and give the size you calculated with the above function at heightForRowAtIndexPath delegete.
Try this sample if I've understood what you want: https://github.com/Dmitriy837/changeTableRowHeighWhenTaped
I suggest you subclass UITableView and support additional property with cell heights in it and change the cell's height when it's taped.

Tableview showing the wrong size of cell [duplicate]

This question already has answers here:
UITableViewCell frame height not matching tableView:heightForRowAtIndexPath:
(3 answers)
Closed 9 years ago.
in table i am setting the height for cell using(heightForRowAtIndexPath)delegate of table view
the code is:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 100;
}
but i am checking the size of cell in delegate method(cellForRowAtIndexPath) and code is:
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Dequeue or create a cell
UITableViewCellStyle style = UITableViewCellStyleDefault;
UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:#"BaseCell"];
if (!cell)
cell = [[UITableViewCell alloc] initWithStyle:style reuseIdentifier:#"BaseCell"] ;
cell.textLabel.text = [NSString stringWithFormat:#"Cell %d", numberOfItems - indexPath.row];
NSLog(#"%f",cell.frame.size.height);
return cell;
}
when i am printing the value(frame of cell)cell on console its giving me 44.00.why this is happening even i am setting height of cell..please explain me and what to do to get the cell oh height 100..thanks in advance
actually i want to make custom type table view which support difrrent orientation of view and it is universal app so it will better to call the cell size in behalf of checking every time (iphone/ipad,diff orintation)....plz help me to accomplish requirement
If the cell is being shown correctly, and by correctly I mean with a height of 100 pixels as you have written in your tableView:heightForRowAtIndexPath:, I'm pretty sure it's because you're asking for the cell's height in the wrong place:
the cell has just been init'd with the default init method, the height returned is therefore the default one, of 44 pixels as nslog prompts in your console, on rendering the delegate sets the right height returned from your method and everything is set up correctly.
I had this issue months ago, for some reasons I needed to know cell's height in the tableView:cellForRowAtIndexPath: method, I came out with a workaround for that: I've stored all rowHeights values in an NSArray, since they were dynamic and different row by row according to their content.
I came out with something like
CGFloat height = [[heightsData objectAtIndex: indexPath.section] objectAtIndex: indexPath.row];
Do you have set your delegate for UITableViewDelegate ?
Try to put any log in your tableView:heightForRowAtIndexPath: method at first to see if your delegate is set.
hi friends dev had given explanation
NSLog(#"%f",[self tableView:tableView heightForRowAtIndexPath:indexPath]);

Calculate custom UITableViewCell height at "indexPath" from -tableView:cellForRowAtIndexPath:?

I adjust the height of a custom UITableViewCell inside the custom class, and I believe I need to use the -tableView:cellForRowAtIndexPath: method to adjust the height of the cell. I am attempting to just adjust the height of the custom cell in the custom cell class, then grab the cell at the given index path cast it, and return the height of that cell like this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
CustomUITableViewCell *cell = (CustomUITableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
return cell.frame.size.height;
}
But I am getting stack overflow. What is a better way around doing this?
The table view delegate will first call heightForRowAtIndexPath: and then the datasource will construct the cell in cellForRowAtIndexPath: after that based on the computed information.
Therefore your approach will not work.
You need to have some logic for computing the height. (E.g. if you are displaying text the height might be dynamic and depend on the amount of text - you could calculate that with an NSString method.)
If you are just displaying a few types of cells with fixed heights, simply define these heights as constants and return the correct height based on the same logic you have in cellForRowAtIndexPath: to decide which cell to use.
#define kBasicCellHeight 50
#define kAdvancedCellHeight 100
- (CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (needToUseBasicCellAtThisIndexPath) {
return kBasicCellHeight;
}
return kAdvancedCellHeight;
}
If it's a storyboard cell, you can call dequeueReusableCellWithIdentifier:. Otherwise, you can just instantiate the cell directly with something like [CustomUITableViewCell alloc] initWithFrame:].
I use this approach (using a prototype cell to calculate height) myself because it allows our designer to modify storyboard cells without requiring code changes.
You may want to adjust your approach based on whether the height is static or dynamic as discussed here.

The best way to calculate UITableViewCell height without contentView frame

SUMMARY
Given that we don't always know what the frame of a cell or its content view is going to be (due to editing, rotation, accessory views etc.), what is the best way to calculate the height in tableView:heightForRowAtIndexPath: when the cell contains a variable height text field or label?
One of my UITableViewController's contains the following presentation:
UITableViewCell with UITextView.
UITextView should be the same width and height as UITableViewCell.
I created the UITableViewCell subclass, and then and initialized it with UITextView (UITextView is a private field of my UITableViewController)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"TextViewCell";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[BTExpandableTextViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier textView:_notesTextView] autorelease];
}
return cell;
}
I implemented the following method in my UITableViewCell subclass:
- (void)layoutSubviews{
[super layoutSubviews];
CGFloat height = [textView.text sizeWithFont:textView.font constrainedToSize:CGSizeMake(textView.frame.size.width, MAXFLOAT)].height + textView.font.lineHeight;
textView.frame = CGRectMake(0, 0, self.contentView.frame.size.width, (height < textView.font.lineHeight * 4) ? textView.font.lineHeight * 4 : height);
[self.contentView addSubview:textView];
}
and of course i implemented the following UITableViewDataSource method (look! I am using self.view.frame.size.width (but really i need UITableViewCell contentView frame width):
- (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath{
CGFloat height = [_notesTextView.text sizeWithFont:_notesTextView.font
constrainedToSize:CGSizeMake(self.view.frame.size.width, MAXFLOAT)].height;
CGFloat groupedCellCap = 20.0;
height += groupedCellCap;
if(height < [BTExpandableTextViewCell minimumTextViewHeightWithFont:_notesTextView.font]){
height = [BTExpandableTextViewCell minimumTextViewHeightWithFont:_notesTextView.font];
}
return height;
}
also I implemented the following method (thats not so important but ill post it anyway, just to explain that cell's height is dynamical, it will shrink or expand after changing text in UITextView)
- (void)textViewDidChange:(UITextView *)textView{
CGFloat height = [_notesTextView.text sizeWithFont:_notesTextView.font
constrainedToSize:CGSizeMake(_notesTextView.frame.size.width, MAXFLOAT)].height;
if(height > _notesTextView.frame.size.height){
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
}
And now, my question is:
After loading view, UITableViewController is calling methods in the following order: (ill remove some, like titleForHeaderInSection and etc for simplification)
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
- (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath{
and only then
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
Look! I should return the correct UITableViewCell height before cellForRowAtIndexPath!
That means: I don't know UITableViewCell contentView frame. And i can't get it programmatically.
This width can be one of:
iPhone plain table, portrait orientation
iPhone plain table, landscape orientation
iPhone grouped table, portrait orientation
iPhone grouped table, landscape orientation
and the same for the iPad ( another 4 values )
And don't forget that contentView frame can be smaller because of UITableViewCell accessoryType, or because of UITableView editing state. (for example if we have UITableViewCell with multiline UILabel of any height in any editing state and with any accessoryView)
So this problem is fundamental: I just can't get cell contentView frame width for constraining, because I should return this height before cell layouts contentView. (And this is pretty logical, by the way) But this contentView frame really matters.
Of course sometimes I can know this width exactly and "hardcode" it
(for example: UITableViewCellAccessoryDisclosureIndicator has 20 px width, and tableView cannot be in editing state, then I can write self.view.frame.size.width - 20 and the task is done)!
Or sometimes contentView is equal to UITableViewController's view frame!
Sometimes I'm using self.view.frame.width in -tableView:heightForRowAtIndexPath: method.. (like now, and it works pretty well, but not perfectly because of grouped UITableView, should subtract some constant values, and they are different for 2 devices * 2 orientations)
Sometimes I have some #defined constants in UITableViewCell (if I know width exactly)...
Sometimes I'm using some dummy pre-allocated UITableViewCell (what is just stupid, but sometimes is pretty elegant and easy for use)...
But I don't like anything of that.
What's the best decision?
Maybe i should create some helper class, that will be initialized with such parameters:
accessory views, device orientation, device type, table view editing state, table view style (plain, grouped), controller view frame, and some other, that will include some constants (like grouped tableView offset, etc) and use it to find the expected UITableViewCell contentView width? ;)
Thanks
Table view uses the tableView:heightForRowAtIndexPath: method to determine its contentSize before creating any UITableViewCellcells. If you stop and think about it, this makes sense, as the very first thing you would do with a UIScrollView is set its contentSize. I have run into a similar problem before, and what I've found is that it is best to have a helper function that can take the content going into the UITableViewCell and predict the height of that UITableViewCell. So I think you will want to create some sort of data structure that stores the text in each UITableViewCell, an NSDictionary with NSIndexPaths as keys and the text as values would do nicely. That way, you can find the height of the text needed without referencing the UITableViewCell.
Although you can calculate heights for labels contained in table view cells, truly dynamically, in '- layoutSubviews' of a UITableViewCell subclass, there's no similar way of doing this (that I know of) for cell heights in '- tableView:heightForRowAtIndexPath:' of a table view delegate.
Consider this:
- (void)layoutSubviews
{
[super layoutSubviews];
CGSize size = [self.textLabel.text sizeWithFont:self.textLabel.font
constrainedToSize:CGSizeMake(self.textLabel.$width, CGFLOAT_MAX)
lineBreakMode:self.textLabel.lineBreakMode];
self.textLabel.$height = size.height;
}
Unfortunately though, by the time '- tableView:heightForRowAtIndexPath:' is called, that is too early, because cell.textLabel.frame is yet set to CGRectZero = {{0, 0}, {0, 0}}.
AFAIK you won't be able to do this neither with content view's frame, nor summing up individual labels' frames...
The only way I can think of is to come up with a convenience class, methods, constants, or such that will try to cover up all possible width in any device orientation, on any device:
#interface UITableView (Additions)
#property (nonatomic, readonly) CGFloat padding;
#end
#implementation UITableView (Additions)
- (CGFloat)padding
{
if (self.formStyle == PTFormViewStylePlain) {
return 0;
}
if (self.$width < 20.0) {
return self.$width - 10.0;
}
if (self.$width < 400.0 || [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return 10.0;
}
return MAX(31.0, MIN(45.0, self.$width * 0.06));
}
#end
Also note that, recently we also have new iPhone 5's 4-inch width (568 instead of 480) in landscape orientation.
This whole thing is pretty disturbing, I know... Cheers.

resizing a UITableViewCell frame issue

I am trying to resize my UITableViewCell's frame via:
[cell setFrame:CGRectMake(cell.frame.origin.x,
cell.frame.origin.y,
cell.frame.size.width,
cell.frame.size.height+25)];
however, it's not resizing after I do this... why is this?
This is weird as if I add a UIToolBar into the cell, it resizes but when I am adding a UIView, it doesn't:
[cell.contentView addSubview:sideSwipeView];
Here's the long and short of it:
Your cell width is determined by the width of the tableview it's in.
[EDIT: If it's a grouped table view, the cell is 20 - 60 pixels narrower than the tableview width, depending if you're using an iPhone, or an iPad.]
Your cell height is determined by the heightForRowAtIndexPath method.
If you're manually setting the cell's frame, it's going to be useless except when you're using a subclassed cell where you want to add subviews based on the cell's dimensions.
Even in this case, it's recommended to get the cell's frame from the tableview by using rectForRowAtIndexPath:(NSIndexPath*)indexPath method and then setting that frame as the cell's frame (after setting the frame's origin Y as 0).
I'm not quite sure about the UIToolBar, but your subview's frame won't change on changing the cell frame.
Maybe if you could tell us what you're trying to achieve, we can suggest a solution for you?
--------------------EDIT--------------------
So you need to dynamically add a subview to a cell on tapping it and resize it's height according to the new subview. This is gonna get hairy so here goes:
In your .h file declare:
BOOL subviewAdded;
In your .m file, in the init, do:
subviewAdded = NO;
Let's assume that you want the cell's height to be 50 without the subview and 100 with the subview. Accordingly, your heightForRow method should be:
- (CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return (subviewAdded?100.0f:50.0f);
}
This means that initially since subviewAdded is NO, all your cells will have the smaller height.
Now, to add a subview to a cell on tapping it, and to change it's height dynamically, do this in your didSelectRow method:
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Get the cell at this indexPath
UITableViewCell *thisCell = [tableView cellForRowAtIndexPath:indexPath];
if(subviewAdded)
{
subviewAdded = NO;
for(int i = 0; i < [thisCell.contentView.subviews count]; i++)
{
UIView *thisSubview = [thisCell.contentView.subviews objectAtIndex:i];
[thisSubview removeFromSuperview];
}
}
else
{
UIView *someView = [[UIView alloc] initWithFrame:someFrame];
[thisCell.contentView addSubview:someView];
[someView release];
subviewAdded = YES;
}
NSMutableArray *array = [NSMutableArray array];
[array addObject:indexPath];
[tableView reloadRowsAtIndexPaths:array
withRowAnimation:UITableViewRowAnimationFade];
}
So what's going to happen here is you're adding a subview to this cell you've tapped. Reloading this cell will call heightForRowAtIndexPath and do a nice little fade animation and change your tableview heights.
IMPORTANT: Ideally, you should maintain an array of NSNumbers with boolean values. The array size should be the same size as the number of tableview cells you have.
In heightForRow, you would then check against this array instead of using a single boolean for the whole tableView. This would ensure that you could have different heights for different cells.
That would look something like:
- (CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath {
BOOL thisBool = (BOOL)[[booleanArray objectAtIndex:indexPath.row] boolValue];
return (thisBool?100.0f:50.0f);
}
I didn't post all that code here since it's implied and what I've posted should put you well on your way to doing the boolean array thing.
Anyway, there you are. I just tested this code myself so it works :)
If you want to increase the height of your cell based on some parameter eg. text, image,
you must implement the heightForRowAtIndexPath method of UITableViewDelegate in your code.
- (CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath

Resources