UILabel's inside UITableViewCell not resizing when orientation changes - ios

I'm populating my UITableView with cells that contain two UILabel's: one of them being the title and the other one being the content. I'm calculating their sizes in heightForRowAtIndexPath: and everything seems cool until I change the orientation. When I change the orientation, the size of each label doesn't seem to change. For example if I'm changing from portrait to landscape, the expected behaviour is to see label's with smaller height (assuming it's a multi-line label). However, the labels' heights remain the same and I get an ugly, disoriented view. The code I've written for this is as follows:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat tableWidth = [tableView bounds].size.width;
ListItem* glossaryItem;
if ([tableView isEqual:self.searchDisplayController.searchResultsTableView])
{
glossaryItem =(ListItem*)[self.searchResults objectAtIndex:indexPath.row];
}
else
{
glossaryItem =(ListItem*)[self.glossary objectAtIndex:indexPath.row];
}
NSString* name = [NSString stringWithFormat:#"<p><b>%#</b> :</p>", glossaryItem.name];
NSString* content = glossaryItem.details[0];
NSAttributedString* nameWithStyle = [HTMLParser parseText:name withFontSize:16 withFontType:#"Light"];
NSAttributedString* contentWithStyle = [HTMLParser parseText:content withFontSize:16 withFontType:#"Light"];
CGRect nameRect = [nameWithStyle boundingRectWithSize:CGSizeMake(screenWidth - 40, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:NULL];
CGRect contentRect = [contentWithStyle boundingRectWithSize:CGSizeMake(screenWidth - 40, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:NULL];
return nameRect.size.height + contentRect.size.height + 33;
}
HTMLParser is a class I wrote to parse text's in HTML format into attributed strings. I'm guessing the problem here is the label's sizes are not recalculated after orientation change. If I add the following code, everything looks fine but this takes too much time for tables with a lot of cells.
- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
[self.tableView reloadData];
}
How can I work around this? I can provide the constraints of my labels if needed.

Setup autoLayout constraints
or, continue to call the reloadData the way you are doing on didRotate...
but size the labels in cellForRow... like below
Set the height for row only in heightForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//Some identifier and recycling stuff
if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) {
//Make labels smaller
}
else {
//Make them longer
}
// save the calculated height for the label
//and size your tableviewrow height accordingly in heightForRowAtIndexPath
}

Related

How to change each UITableViewCell height dynamically?

I'm working on application where i show user comment in UILable and UILable have sizetofit property. i want to change cell height according to UILable height.
My Question is how i change cell height for example first cell height may be 50, second Cell height may be 100 and so on.
For dynamic height of UITableViewCell you have to do below things
Fulfill all constraint requirement in UITableViewCell
Tell your TableView to dynamically layout Height of every Cell with below code
- (void)viewDidLoad {
[super viewDidLoad];
// two magic lines
tableView.estimatedRowHeight = 89
tableView.rowHeight = UITableView.automaticDimension
}
With just two lines of code, you instruct the table view to calculate the cell’s size matching its content and render it dynamically. This self sizing cell feature should save you tons of code and time. You’re gonna love it.
Hope this helps you by tableview methods:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewAutomaticDimension;
}
You can use this method for increase UITableViewCell height dynamically (No AutoLayout)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
NSMutableAttributedString *strName = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:#"%#",strItemName]];
[strName addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:16] range:NSMakeRange(0, strItemName.length)];
CGSize sizeItemName = CGRectIntegral([strName boundingRectWithSize:CGSizeMake(130, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:nil]).size;
int padding = 5;
//your default cell height for ex 55
if (sizeItemName.height < 55)
{
sizeItemName.height = 55;
}
return sizeItemName.height + padding;
}
In your heightForRowAtIndexPath, calculate the dynamic height based on the related cell data.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *data = [self.dataSource objectAtIndex:indexPath.row];
return [MyTableViewCell heightForData:data];
}
Then in your MyTabLeViewCell, write a function as below, let us say the data has the "content" which is the fact for dynamic height. And your tableViewCell defined a UILabel called contentLabel with CONTENT_LABEL_WIDTH
+(CGFloat) heightForData : (NSDictionary *)data{
self.contentLabel.text = [data objectForKey:#"content"];
CGSize contentLabelSize = [self.contentLabel sizeThatFits:CGSizeMake(CONTENT_LABEL_WIDTH, CGFLOAT_MAX)];
return contentLabelSize.height;
//If you want to have a minimum cell height no matter how small your content is, you can use below fmaxf with a pre-defined CELL_MIN_HEIGHT value.
// return fmaxf(CELL_MIN_HEIGHT, height);
}

dynamically size multi-line UILabel and accompanying table view cell programatically

I'm struggling with how to dynamically size a uilabel inside a uitableviewcell and also to adjust the height of the table cell depending on the text I put into the label. I want to display a string in its entirety within the tableview cell, always at the same font size for all cells. This seems pretty standard, but I notice a lot of discussion/debate/discontent on stackoverflow. I got started with these posts:
Resize Custom cell with a UILabel on it based on content text from JSON file (#Seya's answer)
boundingRectWithSize for NSAttributedString returning wrong size (#JOM's answer)
The functions I'm trying to use are copies of what Seya did, barely modified. However, I am seeing that although the function prints out different heights for different cells, all I am seeing is 1 line of text per label even though they should show many lines. Also, strangely the text from one cell seems to display on top of another cell - not sure whether this is related.
My 2 questions: (1) Why do I only see the first line of text? (2) Why is the UILabel/cell not resizing per the different heights I see printed out in the log (or are they resizing but masked by issue #1)?
Here's the three functions I'm using (these along with a xib - could the xib be the problem?):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
JudgeCell *cell = [tableView dequeueReusableCellWithIdentifier:#"JudgeCell"];
if(!cell)
{
[tableView registerNib:[UINib nibWithNibName:#"JudgeCell" bundle:nil] forCellReuseIdentifier:#"JudgeCell"];
cell = [tableView dequeueReusableCellWithIdentifier:#"JudgeCell"];
}
cell.username.text = self.object.answerUser[indexPath.row];
cell.answer.text = self.object.answerArray[indexPath.row];
NSString *text = cell.answer.text;
CGSize constraint = CGSizeMake(50, 20000.0f);
CGSize size = [self frameForText:text sizeWithFont:[UIFont systemFontOfSize:15] constrainedToSize:constraint];
cell.username.frame = CGRectMake(10, 10, 50, size.height); //MAX(size.height, 14.0f));
return cell;
}
-(CGSize)frameForText:(NSString*)text sizeWithFont:(UIFont*)font constrainedToSize:(CGSize)size {
NSMutableParagraphStyle * paragraphStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping;
NSDictionary * attributes = #{NSFontAttributeName:font,
NSParagraphStyleAttributeName:paragraphStyle
};
CGRect textRect = [text boundingRectWithSize:size
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
attributes:attributes
context:nil];
NSLog(#"size is %f ", textRect.size.height);
return textRect.size;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
NSString *text = self.object.answerArray[indexPath.row];
CGSize constraint = CGSizeMake(50, 20000.0f);
CGSize size = [self frameForText:text sizeWithFont:[UIFont systemFontOfSize:15] constrainedToSize:constraint];
CGFloat height = size.height; //MAX(size.height, 14.0f);
NSLog(#"size is %f AT INDEX %d", height, indexPath.row);
return height + 20;
}
Thanks for any advice!
In the end I deleted my associated nib file, and somehow that seemed to do the trick along with the code from this tutorial (which is similar to what I already posted above):
http://www.cimgf.com/2009/09/23/uitableviewcell-dynamic-height/
Note that the methods in the tutorial are sometimes flagged as deprecated. If I find an updated tutorial, I will post.

Cannot change the size of UITextView

I added some tableViewCells to a tableView via Interface Builder, one of them is containing a UITextView.
The tableView is reloaded as an Button gets clicked:
- (void)updateViewWithMessage:(NSString *)message
{
self.someMessage = message;
[self.tableView reloadData];
}
In my cellForRowAtIndexPath method I check for this cell and do the following:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CellIdentifier = kMyCustomCell;
MyCustomCell *contentCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
CGRect textViewFrame = contentCell.contentTextView.frame;
textViewFrame.size.height = [self heightForContentView];
NSLog(#"%f, %f, %f, %f\n", textViewFrame.origin.x,
textViewFrame.origin.y,
textViewFrame.size.width,
textViewFrame.size.height);
contentCell.contentTextView.frame = textViewFrame;
contentCell.contentTextView.text = self.someMessage;
return contentCell;
}
In heightForContentView() I calculate the height:
- (CGFloat)heightForContentView
{
CGSize boundingRectSize = CGSizeMake(kCustomCellWidth - 20, CGFLOAT_MAX);
CGRect textViewRect = [self.someMessage boundingRectWithSize:boundingRectSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:nil
context:nil];
return textViewRect.size.height + 20;
}
The cell, if I set the row Height to something, resizes properly. But the textView in it does not.
If I click my button twice (the updateViewWithMessage: method gets called again) and then it resizes - that's weird!
By the way: NSLog prints the correct sizes, so they are definitely set, but why does this TextView not resize then?
Can someone help me to solve this problem quickly please?
EDIT after comments :
I think you don't resize your UITextView, add this code before to resize contentCell:
contentTextView.frame = CGRectMake(CGRectGetMinX(contentTextView.frame), CGRectGetMinY(contentTextView.frame), CGRectGetWidth(contentTextView.frame), textViewFrame.size.height);
Old Answer :
Maybe you forgot to pass attributes to calculate the new size :
CGRect textViewRect = [self.someMessage boundingRectWithSize:boundingRectSize
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:#{NSFontAttributeName:FONT}
context:nil];
With FONT is your current font.
Have a look here.

Resize tableview NSLayoutConstraint

Hi I have a tableView which I change the width programmatically by his constraint:
self.widthTableLeft.constant = self.view.frame.size.width;
I do this in the viewDidLoad.
In the delegate method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
I need the contentview frame of each cell to calculate the hight of the cell, but when get it, this is the old size, I mean the size if I wouldn't have done the resize.
When the table is showed the size of the table is right but the hight of the cell is wrong.
I have tried to call:
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
[cell.contentView setNeedsLayout];
[cell.contentView layoutIfNeeded];
The question is, before to show the view of my view controller, when is the best place to update the width constraint of a UITableView and to get the right contentView in the cells in the method heightForRowAtIndexPath.
Sorry but I'm lost, could you give me any ideas?
Thanksssss
UPDATE
This is the code in heightForRowAtIndexPath
static const NSInteger ROC_TITLE_LABEL_TAG = 2;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
ROCAgendaItemDetailEntity *agendaItemDetail = [self.listRight objectAtIndex:indexPath.row];
UITableViewCell *cell = [self.tableLeft dequeueReusableCellWithIdentifier:#"cellVideo"];
//I get the label which will make the cell bigger
UILabel *titleLabel = (UILabel *)[cell viewWithTag:ROC_TITLE_LABEL_TAG];
//We init the height title with the min height.
float extraSpaceHeightLabel = 0;
if (agendaItemDetail.agendaItem.title.length > 0) {
//I will use his width to get the hight that I need to write all text
float width = titleLabel.frame.size.width;
CGSize size = CGSizeMake(width, CGFLOAT_MAX);
//we get the size
CGSize sizeTitleLabel = [agendaItemDetail.agendaItem.title sizeWithFont:[ROCTextsInformation imagoMed:15] constrainedToSize:size lineBreakMode:titleLabel.lineBreakMode];
//we substract the current height of the label to know the extra space to write all text
sizeTitleLabel.height -= titleLabel.frame.size.height;
extraSpaceHeightLabel = sizeTitleLabel.height;
}
//The final hight will be the contenViewHeight pluss the extra space needed.
return cell.contentView.frame.size.height + extraSpaceHeightLabel;
}
and this is the cell in the table view
Thanks again
viewDidLoad is too early to try and access the geometries of your views. Instead, access self.view's width in viewDidLayoutSubviews.
#interface CustomViewController ()
#property (nonatomic) BOOL isFirstTimeViewDidLayoutSubviews; // variable name could be re-factored
#end
#implementation CustomViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.isFirstTimeViewDidLayoutSubviews = YES;
}
- (void)viewDidLayoutSubviews
{
// only after layoutSubviews executes for subviews, do constraints and frames agree (WWDC 2012 video "Best Practices for Mastering Auto Layout")
if (self.isFirstTimeViewDidLayoutSubviews) {
// execute geometry-related code...
self.widthTableLeft.constant = self.view.frame.size.width;
}
self.isFirstTimeViewDidLayoutSubviews = NO;
}

UITableViewCell: Begin/Endupdate causes cells to flip out

I have a tableview representing a feed, with three different custom UITableView cells. One (the top one) is solid and should always be there, but the cells underneith that one is either a product or an event cell (loaded from DB). The thing is that the Eventcells have a textview and an imageview that can varie in height, so to view these correctly I calculate the correct height for them and then set the height in heightForRowAtIndexPath. I need to update the cell with its new height somehow, so I do an tableview begin/end update. However when I do this for every cell each time its loaded into view, all the cells start bouncing around and change content when I scroll the tableview.
Here is my CellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
return [self loadJobInfoCell:indexPath];
} else if (indexPath.section == 1) {
if ([[jobDetailsArray objectAtIndex:indexPath.row] isKindOfClass:[JobProduct class]]) {
return [self loadProductCell:indexPath];
} else if ([[jobDetailsArray objectAtIndex:indexPath.row] isKindOfClass:[JobEvent class]]) {
static NSString *CellIdentifier = #"EventCell";
EventCell *cell = [tableViewRef dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[EventCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
JobEvent *currentEvent = [jobDetailsArray objectAtIndex:indexPath.row];
// setting labels and stuff here
// Is there an image to this event?
if (![currentEvent.EventPicture isEqual:[NSNull null]]) {
[[cell largeImage] setImage:currentEvent.EventPicture];
[[cell largeImageHeightConstraint] setConstant:currentEvent.EventPicture.size.height];
NSNumber *height = [NSNumber numberWithFloat:currentEvent.EventPicture.size.height];
[largeImagesDictionary setObject:height forKey:[NSString stringWithFormat:#"%d", indexPath.row]];
} else {
[[cell largeImageHeightConstraint] setConstant:0.f];
}
// set correct height for the textview
NSDictionary *attributes = #{NSFontAttributeName: [UIFont systemFontOfSize:14]};
CGRect paragraphRect = [cell.tvText.text boundingRectWithSize:CGSizeMake(204.f, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:attributes context:nil];
[[cell tvTextHeightConstraint] setConstant:paragraphRect.size.height+16.f];
NSNumber *height = [NSNumber numberWithFloat:[[cell tvTextHeightConstraint] constant]];
[eventTextHeightDictionary setObject:height forKey:[NSString stringWithFormat:#"%d", indexPath.row]];
[tableViewRef beginUpdates];
[tableViewRef endUpdates];
return cell;
}
}
return nil;
Without the begin/endupdates it works fine, though the cells are not the correct height and get cut of. Can I somehow update the height without reloading the table, or is there a better solution to the whole situation? Ive tried keeping track of which cells have gotten their update but that doesn't work, it still messes up the order, height and content. I've tried every combination of solutions I could possibly think of, but being a novice iOS developer im not even sure im taking the correct approach to this problem.
Thanks very much in advance.
EDIT:
Man, Im stupid.. I've sat and calculated heights in cellforrowatindex insted of in heightforrowatindex and passed the data inbetween with nsdictionaries. I solved this with autolayout and pre-calculating the height of the data in heightforrowatindex.
I'm not exactly sure of your setup, but the way I've done this, is to set up the constraints in IB so that the image view and text view will expand automatically as the cell does. By doing it this way, I don't have to do any size changes for the image view or text view in code, just the cell size. My cell setup looks like this:
The image view is centered in the x direction and has a constraint to the top of the cell and one from the bottom to the top of the text view. The text view has constraints to the sides and to the bottom of the cell. I put a dummy image in the image view, and selected "Size To Fit Content" from the Editor menu -- this cause the height and width constraints for the image view to be deleted.
In code, I calculate the sizes for the image view and text view, then return the sum of their heights (plus a fudge factor) in heightForRowAtIndexPath. Here is the code for a sample app:
- (void)viewDidLoad {
[super viewDidLoad];
self.theData = #[#{#"text":#"jkkjhkj kh k jk h hkj hjkhkjh hjkh jk hhkjhjkh jkh hkj hkjh hkjhjkhhkk jk jkh jkhkhkjhjhkjhkjhkkjhjjhk kjhkjh jkh hk h kj h jkh jkh kjh kh hjkhk jhjk", #"Pic":#"pic1.jpg"},#{#"text":#"fjhg lfkgh gjk gjk glkjfhgjkhgjkgh sjkghsjkgsjgjgk jgk hg hdgjlhjhjgjg fgjklfg fghjgk gjlkg hjgh jg jlkgljsdkggjlglgjdlkg hgjlgjfkghjg ljhfg jlskfdg hjgjlkgjlkdf gjfghjlkfgljkgjlkdgjdfghjdgjglhjkg hljkg ljkgljkfgljkgljksdgljkfgjlfg ljfglldkfjgh ljkgjlkf dgfghslfjdgklfjgljfdfgl", #"Pic":#"pic2.tiff"},#{#"text":#"jdkh lj flfh ljs fajlh ljds f", #"Pic":#"pic3.tiff"}];
[self.tableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.theData.count;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGSize textViewSize = [self.theData[indexPath.row][#"text"] sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:CGSizeMake(280.f, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
UIImage *pic = [UIImage imageNamed:self.theData[indexPath.row][#"Pic"]];
CGSize imageViewSize = pic.size;
return textViewSize.height + imageViewSize.height + 40;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
RDCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.tv.text = self.theData[indexPath.row][#"text"];
cell.iv.image = [UIImage imageNamed:self.theData[indexPath.row][#"Pic"]];
return cell;
}

Resources