Labels aligning in UITableViewCell - iphone

I use UITableView with cells created using UITableViewCellStyleSubtitle. Every cell's height is dynamically adjusted using the
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
delegate method.
The problem you can see on the picture
(note: image updated)
http://img.skitch.com/20090715-g7srxgee2d7fhi5rab2wufrdgm.png
How to set up align of textLabel and detailTextLabel to the top of the cell? (I really don't wont to do it by subclassing UITableViewCell and overriding layoutSubviews)
Thanx

Well, this one cost me an afternoon, but I think I figured it out. As far as I can tell, this appears to be a bug in how UITableViewCell is laying out the textLabel and detailTextLabel. When you set the row height, it seems to allocate equal height to the two labels, which means that you get exactly the behavior you're seeing above, even though detailTextLabel needs more room. Here are the two things I did to fix the problem. I had to subclass UITableViewCell to fix it, but it's a minimal amount of code.
First, make sure you're calculating the height of each row properly. Put this method into your table view delegate. Replace the font methods with your own:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellDetailText = [[self itemForIndexPath: indexPath] detailDescription];
NSString *cellText = [[self itemForIndexPath: indexPath] description];
// The width subtracted from the tableView frame depends on:
// 40.0 for detail accessory
// Width of icon image
// Editing width
// I don't think you can count on the cell being properly laid out here, so you've
// got to hard code it based on the state of the table.
CGSize constraintSize = CGSizeMake(tableView.frame.size.width - 40.0 - 50.0, CGFLOAT_MAX);
CGSize labelSize = [cellText sizeWithFont: [self cellTextLabelFont] constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
CGSize detailSize = [cellDetailText sizeWithFont: [self cellDetailTextLabelFont] constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
CGFloat result = MAX(44.0, labelSize.height + detailSize.height + 12.0);
return result;
}
Then, subclass UITableViewCell and override layoutSubviews:
#import "UITableViewCellFixed.h"
#implementation UITableViewCellFixed
- (void) layoutSubviews {
[super layoutSubviews];
self.textLabel.frame = CGRectMake(self.textLabel.frame.origin.x,
4.0,
self.textLabel.frame.size.width,
self.textLabel.frame.size.height);
self.detailTextLabel.frame = CGRectMake(self.detailTextLabel.frame.origin.x,
8.0 + self.textLabel.frame.size.height,
self.detailTextLabel.frame.size.width,
self.detailTextLabel.frame.size.height);
}
#end

Here is another solution without subclassing cells, so it certainly works with different table styles. (I haven't checked other solutions.) The title and detail strings are declared in my UITableViewController header and already defined. They aren''t very long, certainly well within height 4000!
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGRect frame = [UIScreen mainScreen].bounds;
CGFloat width = frame.size.width;
int section = indexPath.section;
int row = indexPath.row;
NSString *title_string = [title_strings_array objectAtIndex:row];
NSString *detail_string = [detail_strings_array objectAtIndex:row];
CGSize title_size = {0, 0};
CGSize detail_size = {0, 0};
if (title_string && [title_string isEqualToString:#""] == NO ) {
title_size = [title_string sizeWithFont:[UIFont systemFontOfSize:22.0]
constrainedToSize:CGSizeMake(width, 4000)
lineBreakMode:UILineBreakModeWordWrap];
}
if (detail_string && [title_string isEqualToString:#""] == NO ) {
detail_size = [detail_string sizeWithFont:[UIFont systemFontOfSize:18.0]
constrainedToSize:CGSizeMake(width, 4000)
lineBreakMode:UILineBreakModeWordWrap];
}
CGFloat title_height = title_size.height;
CGFloat detail_height = detail_size.height;
CGFloat content_size = title_height + detail_height;
CGFloat height;
switch ( section ) {
case 0:
height = content_size;
break;
//Just in case
default:
height = 44.0;
break;
}
return height;
}

However you're sizing your cells, you should do it with the various sizing methods of NSString. That way, you can determine exactly how tall to make the cell and avoid the whitespace.

If it turns out that the textLabel and detailTextLabel are laid out using autoresizing masks, maybe you can do this when you return the cell:
cell.textLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;
cell.detailTextLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
If this works, it's a bit easier than subclassing the cell. I haven't tried it though.

Related

Get UITableView's height

I have created a UITableview with custom UITableViewCell.The UITableViewCell contains UILabels and I have calculated the height of each cell based on the cells height.But how can I get the tableview height?Because based on the tableView's height I need to calculate
the height of scrollView
I have created a UITableView like this when a button is clicked:
-(IBAction)btnClicked:(id)sender
{
self.tableView=[[UITableView alloc] initWithFrame:CGRectMake(0,150,327,[arr5 count]*205) style:UITableViewStylePlain];
self.tableView.delegate=self;
self.tableView.dataSource=self;
self.tableView.scrollEnabled = NO;
[self.view addSubview:self.tableView];
float fscrview = 150 + self.tableView.frame.size.height + 20;
testscroll.contentSize=CGSizeMake(320, fscrview);
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *city1 = city.text;
UIFont *font = [UIFont fontWithName:#"Helvetica" size:14.0];
CGSize constraintSize = CGSizeMake(280.0f, MAXFLOAT);
CGSize bounds = [city1 sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
//similarly calculated for all
return (CGFloat) cell.bounds.size.height + bounds.height+bounds1.height+bounds2.height+bounds3.height+bounds4.height+bounds5.height;
}
I am able to get tableViewCells height through this.How do I calculate/set the overall tableView's height after this?
And how to calculate ScrollView's height based on tableView's row/height?
Use contentSize.height property of UITableView.
I think you want to set the whole tableview with content size and then set the scrollview size related content of UITableView and for this use bellow code...
After add data or reloadData in UITableView just set bellow code..
yourTableView.frame = CGRectMake(yourTableView.frame.origin.x,yourTableView.frame.origin.y, yourTableView.frame.size.width, yourTableView.contentSize.height);
float ftbl = yourTableView.frame.origin.y + yourTableView.contentSize.height + 15;
yourScrollView.contentSize=CGSizeMake(320, ftbl);
UPDATE:
self.tableView=[[UITableView alloc] initWithFrame:CGRectMake(0,150,327,[arr5 count]*205)style:UITableViewStylePlain];
self.tableView.delegate=self;
self.tableView.dataSource=self;
[testscroll addSubview:self.tableView]; /// add this tableview in scrollview not in self.view
[self.tableView reloadData];
self.tableView.frame = CGRectMake(self.tableView.frame.origin.x, self.tableView.frame.origin.y, self.tableView.frame.size.width, self.tableView.contentSize.height);
float ftbl = self.tableView.frame.origin.y + self.tableView.contentSize.height + 15;
testscroll.contentSize=CGSizeMake(320, ftbl);
Very simplest way to get your UITableView height
- (CGFloat)tableViewHeight
{
[tblData layoutIfNeeded];
return [YOUR_TABLE_NAME contentSize].height;
}
on iOS 8+ this worked to get the table view Height
CGFloat tableViewHeight = tableView.bounds.size.height;
Try this: tableView.backgroundView.bounds.size.height Should work
Logic:
UITableView has a property, backgroundView which is "A table view’s background view is automatically resized to match the size of the table view."
backgroundView is a UIView which has the property bounds which is a CGRect that "defines the size and position of the view."
CGRect has a size property, size has a height property
QED
You also can use KVO to observer tableview's contentSize property and adjust what you need in other views.
Put it somewhere, e.g. in viewDidLoad:
[self.tableView addObserver:self forKeyPath:#"contentSize" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionInitial context:nil];
Then implement observer:
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if (object == self.tableView) {
self.scrollViewHeightConstraint.constant = self.tableView.contentSize.height;
}
else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}

How to extend uilabel height as per content?

I want to change the uilabel height as per content and display it in a uitableview cell, there is a custom cell and cell is expand as per uilabel height
When button is pressed then and then cell height is expand as per the uilabel height
Thank you in Advance :-)
//Calculate the size based on the font and linebreak mode of your label
CGSize maxLabelSize = CGSizeMake(300,9999);
CGSize expectedLabelSize = [myString sizeWithFont:myLabel.font
constrainedToSize:maxLabelSize
lineBreakMode:myLabel.lineBreakMode];
//adjust the label the the new height.
CGRect newFrame = myLabel.frame;
newFrame.size.height = expectedLabelSize.height;
myLabel.frame = newFrame;
Try this
[label sizeToFit];
Note that label will increase it's height keeping it's width the same.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *text = displayText;
//CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);
CGSize constraint = CGSizeMake(tableView.frame.size.width - 30, 20000.0f);
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:15.0f] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
CGFloat height = MAX(size.height, 44.0f);
return height + 30;
}
Calculate the size of the label text & than set coordinates as
CGSize labelWidth=[_label.text sizeWithFont:[UIFont fontWithName:#"Verdana" size:12] constrainedToSize:CGSizeMake(200, 15) lineBreakMode:UILineBreakModeTailTruncation];

UILabel with -[sizeWithFont:constrainedToSize:lineBreakMode] is cutting off words

UILabel inside of a UITableViewCell. On cell tap, height expands and a second UILabel appears with various amounts of data.
The problem is, if the line wraps, sizeWithFont:constrainedToSize:lineBreakMode: does not return the proper height and cuts off the bottom line.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
labelExpanded = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
labelExpanded.font = [UIFont boldSystemFontOfSize:11];
labelExpanded.textAlignment = UITextAlignmentLeft;
labelExpanded.lineBreakMode = UILineBreakModeWordWrap;
labelExpanded.numberOfLines = 0;
[cell.contentView addSubview:labelExpanded];
CGSize constraint = CGSizeMake(300, 20000);
CGSize size = [expandedDetails sizeWithFont:[UIFont boldSystemFontOfSize:11] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
[labelExpanded setText:expandedDetails];
[labelExpanded setFrame:CGRectMake(16, 30, 248, size.height)];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(// Row is Expanded)
{
CGSize constraint = CGSizeMake(300, 20000);
CGSize size = [sameTextAsLabel sizeWithFont:[UIFont boldSystemFontOfSize:11] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
// I found +8 to height added nice padding
CGFloat height = size.height + 8;
}
else // Row is not expanded
return 30;
}
The same text is fed to both, it consists of a string with multiple new lines \n. If the line is longer than width, word wrap does its job but when calculating its dynamic height, it fails to include the wrapped line.
If I add any value to the [labelExpanded setFrame:height] such as [size.height + 30] my wrapped line shows up. However, if the line isn't wrapped, it adds unnecessary whitespace.
I haven't been able to find a solution online for this.
One thing I note is that you are calculating the size based on a width of 300, but sizing the label at 248.
CGSize constraint = CGSizeMake(300, 20000);
CGSize size = [expandedDetails sizeWithFont:[UIFont boldSystemFontOfSize:11] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
[labelExpanded setText:expandedDetails];
[labelExpanded setFrame:CGRectMake(16, 30, 248, size.height)];
Why is the label frame width 248 and constraint width is 300?

How to deal with screen position constants in iPhone code?

I have a requirement to fill cells with varying length wraparound text. My current routine is handling this satisfactorily, however, I am concerned about the use of a couple of constants being used.
The values 10 and 260 below represent the margin and cell width expected, but they are only accurate for standard definition resolution in portrait orientation.
Is there some screen/table object that provides me these values as metrics I could use instead of the constants?
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell;
UILabel *lbl;
CGRect frame;
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero
reuseIdentifier:AnswerIdentifier] autorelease];
frame.origin.x = 10;
frame.origin.y = 10;
frame.size.height = [self textHeight:indexPath];
frame.size.width = 260;
lbl = [[UILabel alloc] initWithFrame:frame];
lbl.lineBreakMode = UILineBreakModeWordWrap;
lbl.numberOfLines = 0;
[cell.contentView addSubview:lbl];
[lbl release];
return cell;
}
- (CGFloat)textHeight:(NSIndexPath *)indexPath {
CGSize textSize;
CGSize constraintSize;
CGFloat height;
NSString *theText = [self cellText:indexPath];
constraintSize = CGSizeMake(260.0f, MAXFLOAT);
textSize = [theText sizeWithFont:kCELL_FONT
constrainedToSize:constraintSize
lineBreakMode:UILineBreakModeWordWrap];
height = textSize.height;
return height;
}
The table obviously needs to fit in the frame of the containing view (which might be the top level view or the window), so I might use percentage or pixel offsets from that rect.
Try using
[[UIScreen mainScreen] applicationBounds];
This will return a CGRect for the current window size. Then, for your code, you might use:
CGRect screenRect = [[UIScreen mainScreen] applicationBounds];
frame.origin.x = 10;
frame.origin.y = 10;
frame.size.height = [self textHeight:indexPath];
frame.size.width = screenRect.size.width - 60.0;
Assuming that the pixel offsets in x are fixed, with flexible label width.

heightForRowAtIndexPath for longer NSStrings

I have a UITableView (Grouped!) and need to calculate the height of two styles of cells: UITableViewCellStyleDefault and UITableViewCellStyleValue2.
This is how I do it for UITableViewCellStyleDefault:
CGSize textSize = {300.f, 200000.0f};
CGSize size = [myTextString1 sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:textSize lineBreakMode:UILineBreakModeWordWrap];
size.height += 30.0f;
result = MAX(size.height, 44.0f);
And for UITableViewCellStyleValue2:
CGSize textSize = {207.f, 200000.0f};
CGSize size = [myTextString2 sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:textSize lineBreakMode:UILineBreakModeWordWrap];
size.height += 30.0f;
result = MAX(size.height, 44.0f);
My issue it that they return incorrect heights and I think it's the textSize where I use incorrect numbers. With long texts, the bottom part gets cut short (usually just a line with a few words), and for both CellStyles they have weird spacing above and below the text in the cell.
For UITableViewCellStyleValue2 I got the width size (207.0f) from making the text.backgroundColor red and then just calculating the size of the box. 300.0f width for UITableViewCellStyleDefault is how big the cell is with UITableViewStyleGrouped.
Does anyone know which values I need to use to properly calculate the size of an NSString and thereby get appropriate height for my cells?
Thanks
Here is the code I am using for this. It works like a charm for one type of cell. It may have some useful parts for your application.
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath *) indexPath {
AppAppDelegate *appDelegate = (AppAppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *Text = ([[appDelegate.myTextSectionsDelegateDict objectAtIndex:indexPath.section] objectForKey:#"Text"]);
UIFont *cellFont = [UIFont fontWithName:#"Helvetica" size:17.0];
CGSize constraintSize = CGSizeMake(280.0f, MAXFLOAT);
CGSize labelSize = [Text sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
return labelSize.height + 15;}
When you create your cell, are you using the same font as the one you use to measure?
I used the tutorial on this page and everything worked for me. It might be useful to you too:
It seeems you've figured out that you were using the wrong text size. You might want to just use the font and lineBreakMode properties of the label to avoid this problem in the future, especially if you change them in the cell at a later time. Also, for readability's sake, I would avoid adding to the height before returning a number. Instead I'd try something like this:
CGSize textSize = CGSizeMake( 300.0, 1979 );
CGSize size = [ myTextString1 sizeWithFont:[[ cell textLabel ] font ]
constrainedToSize:textSize
lineBreakMode:[[ cell textLabel ] lineBreakMode ]];
result = MAX( size.height + 30, 44.0f );
I used 1979 because according to the documentation, one should not return values larger than 2009.
If you want to calculate the height properly for cellvalue2 you should use:
[UIFont boldSystemFontOfSize:15.0f] and as linebreakmode: NSLineBreakByTruncatingTail
If you don't use bold, the text will fill correctly but you will lose the correct vertical padding.
The rest of your calculation are correct.
When is working for me is to compute the constraintSize based on the width of the table view:
CGSize constraintSize = CGSizeMake(tableView.frame.size.width * 0.6, 2009);
CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
Easiest way to do this:
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
CGFloat result = 10.0f;
if(indexPath.section == 1) // here i compare the sections
{
result = 400.0f;
}
return result;
}
Return the values based on the sections you have. This will give you custom row heights.