I am building a custom, in-application settings view based on UITableViewCellStyle1 (or so).
I am trying to dynamically calculate the width of the left label (title) of a cell to determine how wide my text field on the right can be.
When I launch the app in the simulator and the view loads, the width of the title labels is zero until I scroll down the view and a cell is not visible, then when I scroll back up the size adjusts as expected. I believe this might have to do something with cell reusage.
I need the title labels in the cells to have their correct width when the view loads, not after they have gone out of sight one time.
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
static NSString* reuseIdentifier = #"SettingsCell";
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if ( !cell )
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier] autorelease];
[cell setBackgroundColor:[UIColor baeDarkGrayColor]];
UILabel* cellLabel = [cell textLabel];
[cellLabel setTextColor:[UIColor whiteColor]];
[cellLabel setBackgroundColor:[UIColor clearColor]];
[cell setUserInteractionEnabled:YES];
[cell setSelectionStyle:UITableViewCellSelectionStyleGray];
}
// Populate cell with corresponding data.
NSDictionary* tableSection = [_tableData objectAtIndex:indexPath.section];
// Set the label
UILabel* textLabel = [cell textLabel];
NSString* labelString = [[tableSection objectForKey:#"itemTitles"] objectAtIndex:indexPath.row];
[textLabel setText:labelString];
// CGSize constrainedSize;
// constrainedSize.width = MAXFLOAT;
// constrainedSize.height = MAXFLOAT;
CGSize textLabelSize = [textLabel.text sizeWithFont:textLabel.font /*constrainedToSize:constrainedSize*/];
NSLog(#"text label width: %f", textLabelSize.width);
// Set the accessory view
BAESettingsTableCellAccessoryType accessoryType = [[[tableSection objectForKey:#"itemAccessories"] objectAtIndex:indexPath.row] integerValue];
switch ( accessoryType )
{
case BAESettingsTableCellAccessoryDisclosureIndicator:
{
UIImageView* disclosureView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"AccessoryDisclosure.png"]];
[cell setAccessoryView:disclosureView];
[disclosureView release];
break;
}
case BAESettingsTableCellAccessoryOnOffSwitch:
{
UISwitch* onOffSwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
[onOffSwitch setOn:YES];
[cell setAccessoryView:onOffSwitch];
[onOffSwitch release];
break;
}
case BAESettingsTableCellAccessoryNone:
// default:
// break;
{
CGRect cellFrame = [cell frame];
float detailTextFieldWidth = cellFrame.size.width - ( textLabelSize.width + 50 );
float detailTextFieldHeight = cellFrame.size.height - 1;
NSLog(#"detail text field calculated frame width, height, %f, %f", detailTextFieldWidth, detailTextFieldHeight);
CGRect frame = CGRectMake(cellFrame.origin.x, cellFrame.origin.y, detailTextFieldWidth, detailTextFieldHeight);
UITextField* textField = [[UITextField alloc] initWithFrame:frame];
NSString* detailLabelString = [[tableSection objectForKey:#"itemDetailStrings"] objectAtIndex:indexPath.row];
textField.text = detailLabelString;
textField.textColor = cell.detailTextLabel.textColor;
textField.textAlignment = UITextAlignmentRight;
textField.borderStyle = UITextBorderStyleRoundedRect;
[cell setAccessoryView:textField];
[textField release];
break;
}
}
return cell;
}
Explicitly setting the font solves your problem.
CGSize textLabelSize = [textLabel.text sizeWithFont:[UIFont systemFontOfSize:17]];
Gory Detals:
When the tableview first loads, this snippet returns zero width due to the font size being 0.
[textLabel.text sizeWithFont:textLabel.font]
I figured this out by displaying the point size of the cell font.
NSLog(#"font size: %f", cell.textLabel.font.pointSize);
The point size was zero until the cell went off screen and came back, just as you described.
I completely agree that it's sufficiently annoying that it should be a bug. Why would the nice folks at Apple ever ask for the cell height without ensuring the cell is completely set up. Silly! However, instead of using a deprecated function, try:
[cell layoutIfNeeded];
Calling this method has the same effect with the cell being set up including the bounds, cell font, etc. Therefore, any calculation after this has access to the fully set up cell. Here's the whole code:
-(CGFloat) tableView:(UITableView *) tableView heightForRowAtIndexPath:(NSIndexPath *) indexPath {
// Find the cell for this index path
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
CGFloat cellHeight = cell.frame.size.height;
// Calculate text size after forcing a layout
[cell layoutIfNeeded];
CGSize textSize = [cell.textLabel.text sizeWithFont:cell.textLabel.font constrainedToSize:CGSizeMake(cell.contentView.bounds.size.width, 99999) lineBreakMode:cell.textLabel.lineBreakMode];
// Only change the cell height if the text forces a growth
if (textSize.height > cellHeight) {
cellHeight = textSize.height;
}
return cellHeight;
}
I found the same issue, but I've found a workaround. FYI I've raised an bug with Apple (Bug ID# 7535066). Lex, in your example, if you use cell.font instead of textLabel.font, it should work, although you will get a compiler warning, as this is a deprecated method call. I'm not sure if you'll be able to see the details of the bug I raised, so I'm pasting the details here:
iPhone SDK 3.1.2 (7D11) NSString UIKit Additions method sizeWithFont returns nil
SUMMARY
I am decorating UITableViewCells in the UITableView delegate method:
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
I wish to find length of the text already in the cell, to correctly format the new view, so I am calling the NSString UIKit Additions method:
(CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size
When I use cell.textLabel.font for the font argument, it always returns an empty CGSize
IF I use the DEPRECATED method, cell.font for the argument, it DOES return the CGSize value as expected.
STEPS TO REPRODUCE
1. Create a UITableViewController with a TableView, and populate it with some sample data
2. In the stubbed method cellForRowAtIndexPath, after the comment //Set up the cell, and where the cell.textLabel.text is populated, put the following:
CGSize size = [cell.textLabel.text sizeWithFont:[[cell textLabel] font] constrainedToSize:CGSizeMake(320, 480)];
NSLog(#"cell frame: %f %f", size.width, size.height);
CGSize size2 = [cell.textLabel.text sizeWithFont:[cell font] constrainedToSize:CGSizeMake(320, 480)];
NSLog(#"DEPRECATED cell frame: %f %f", size2.width, size2.height);
EXPECTED RESULTS
If anything, you would expect the call that uses the Deprecated method to retrieve the font, to fail, or at least both to return the same result.
ACTUAL RESULT:
2010-01-12 22:06:36.645 PrefTest[9659:207] cell frame: 0.000000 0.000000
2010-01-12 22:06:36.646 PrefTest[9659:207] DEPRECATED cell frame: 88.000000 24.000000
WORKAROUND & ISOLATION:
As noted, use the deprecated method to get the font. But it gets stranger!
Reverse the two methods I gave above:
CGSize size2 = [cell.textLabel.text sizeWithFont:[cell font] constrainedToSize:CGSizeMake(320, 480)];
NSLog(#"DEPRECATED cell frame: %f %f", size2.width, size2.height);
CGSize size = [cell.textLabel.text sizeWithFont:[[cell textLabel] font] constrainedToSize:CGSizeMake(320, 480)];
NSLog(#"cell frame: %f %f", size.width, size.height);
The results are now:
2010-01-12 22:06:36.645 PrefTest[9659:207] cell frame: 88.000000 24.000000
2010-01-12 22:06:36.646 PrefTest[9659:207] DEPRECATED cell frame: 88.000000 24.000000
It seems that just referencing [cell font] is enough to force [[cell textLabel] font] to work correctly.
Use [myLbl sizeToFit]; to automatically resize the label.
Here is the sample code.
UILabel *lbl1, *lbl2;
if (cell == nil) {
UILabel *lbl1 = [[UILabel alloc] init];
[lbl1 setFont:[UIFont boldSystemFontOfSize:20]];
[cell.contentView addSubview:lbl1];
lbl1.frame = CGRectMake(3, 10, 0, 0);
lbl1.tag = 10;
[lbl1 release];
UILabel *lbl2 = [[UILabel alloc] init];
[lbl2 setFont:[UIFont systemFontOfSize:20]];
[cell.contentView addSubview:lbl2];
lbl2.tag = 20;
[lbl2 release];
}
else {
lbl1 = (UILabel*)[cell.contentView viewWithTag:10];
lbl2 = (UILabel*)[cell.contentView viewWithTag:20];
}
lbl1.text = #"Hello ";
[lbl1 sizeToFit];
lbl2.text = #"World";
[lbl2 sizeToFit];
lbl2.frame = CGRectMake(lbl1.frame.origin.x+lbl1.frame.size.width+5,
lbl1.frame.origin.y,
lbl2.frame.size.width,
lbl1.frame.size.height);
Related
I have a particularly problem that i can't seem to solve i have looked at my code for a few hours and can't seem to fix the problem.
When i add messages to my tableview the messages overflows the wrapper within the UITableViewCell and sometimes the wrapper gets cutoff by the cell.
How do i make sure that my wrapper is always big enough to hold the txt (lblMessage)
and the cell is big enough to hold the wrapper.
Thanks in advance !
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat height = [self heightForMessage:indexPath];
if(height < 50)
{
NSLog(#"Normal : %d , row : %d", 100,indexPath.row);
return 100;
}
else
{
NSLog(#"Custom : %f , row : %d", 70 + height,indexPath.row);
return 70 + height;
}
}
-(CGFloat)heightForMessage:(NSIndexPath *)indexPath
{
MessageObject * model = [self.chats objectAtIndex:indexPath.row];
CGSize size = [model.message sizeWithFont:[UIFont fontWithName:#"HelveticaNeue-Light" size:12] constrainedToSize:CGSizeMake(290, 100000) lineBreakMode:NSLineBreakByWordWrapping];
return size.height;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * NewsCellIdentifer = #"NewsCellIdentifier";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:NewsCellIdentifer];
UILabel * lblContact;
UILabel * lblMessage;
UILabel * lblDate;
UIView * wrapper;
if (cell == nil)
{
/* Top menu for login */
wrapper = [[UIView alloc] initWithFrame:CGRectMake(10, 5, 300, 90)];
[wrapper setBackgroundColor:[self.delegate.color colorWithAlphaComponent:0.6f]];
[wrapper.layer setCornerRadius:6.0f];
[wrapper.layer setShadowOffset:CGSizeMake(0, 2)];
[wrapper.layer setShadowRadius:2.0f];
[wrapper.layer setShadowOpacity:0.5f];
[wrapper viewWithTag:19];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:NewsCellIdentifer];
/* Contact Name */
lblContact = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, 150, 30)];
[lblContact setFont:[UIFont fontWithName:#"HelveticaNeue-Medium" size:13]];
[lblContact setTag:13];
[lblContact setTextColor:[self.delegate colorFromHexString:#"#fffbff"]];
[lblContact setBackgroundColor:[UIColor clearColor]];
/* Received Message */
lblMessage = [[UILabel alloc] initWithFrame:CGRectMake(10, 20, 280, 50)];
[lblMessage setNumberOfLines:0];
[lblMessage setLineBreakMode:NSLineBreakByWordWrapping];
[lblMessage setBackgroundColor:[UIColor clearColor]];
[lblMessage setFont:[UIFont fontWithName:#"HelveticaNeue-Light" size:12]];
[lblMessage setTextColor:[self.delegate colorFromHexString:#"#fffbff"]];
[lblMessage setTag:14];
/* Date received */
lblDate = [[UILabel alloc] initWithFrame:CGRectMake(10, 65, 65, 30)];
[lblDate setText:#"4 hours ago"];
[lblDate setFont:[UIFont fontWithName:#"HelveticaNeue-Light" size:11]];
[lblDate setTextColor:[self.delegate colorFromHexString:#"#fffbff"]];
[lblDate setTag:15];
/* Subview Logic */
[wrapper addSubview:lblContact];
[wrapper addSubview:lblMessage];
[wrapper addSubview:lblDate];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[cell.contentView addSubview:wrapper];
}
else
{
lblContact = (UILabel *)[cell.contentView viewWithTag:13];
lblMessage = (UILabel *)[cell.contentView viewWithTag:14];
wrapper = [cell.contentView viewWithTag:19];
}
MessageObject * model = [self.chats objectAtIndex:indexPath.row];
CGFloat height = [self heightForMessage:indexPath];
[lblMessage setFrame:CGRectMake(10, 25, 280, height)];
[lblMessage setText:model.message];
[lblContact setText:model.clientModel.firstName];
if(height < 50)
{
NSLog(#"wrapper size : %d for row %d ", 90, indexPath.row);
[wrapper setFrame:CGRectMake(10, 5, 300, 90)];
}
else
{
NSLog(#"wrapper size : %f for row %d ", height + 60, indexPath.row);
[wrapper setFrame:CGRectMake(10, 5, 300, height + 60)];
[lblDate setFrame:CGRectMake(10, height + 20, 65, 30)];
}
[cell.contentView setBackgroundColor:[UIColor greenColor]];
[cell setBackgroundColor:[UIColor clearColor]];
return cell;
}
Consider significantly improve your code...Cause it's almost unreadable now
1) Get rid of hardcode e.g. 20, 280. Instead consider using constants (CONTACT_LABEL_TO_MESSAGE_LABEL_OFFSET, MESSAGE_LABEL_WIDTH)
2) Get rid of adding custom subviews to UITableViewCell and tagging them with hardcoded (see 1) numbers - instead subclass UITableViewCell, Prototype it in .xib or .storyboard, make convinient methods for your class - e.g setDate:, setMessage:, setContactName:. All frames should be calculated in your subclass -layoutSubviews method.
At first look:
1) you set width of 280 to lblMessage, when you constrained it's text to width of 290, it causes wrong height calculation
One of the errors is when you are creating the wrapper view for a new cell the line:
[wrapper viewWithTag:19];
should be:
[wrapper setTag:19];
This probably causes wrapper to be nil when you attempt to resize it on a reused cell.
I recommend using a prototype cell and computing cell heights by literally configuring the prototype with the data for the given index path. Just dequeue a cell (yes, you can do this outside of cellForRowAtIndexPath) and hold onto it for doing height calculations. For dynamic height labels, you'd typically call [label sizeToFit] in the cell's configuration logic. There is a more detailed discussion of this technique in this question.
If you want to take a look at a 3rd-party library, my TLIndexPathTools library provides a base table view class TLTableViewController with built-in support for dynamic row height using prototypes:
For static, non-data driven height, it will return the default height of the prototype. So, if your prototype comes from the Storyboard, it will honor the Storyboard custom cell heights.
For dynamic, data-driven height, any custom cell that implements the TLDynamicHeightView protocol will automatically have the dynamic height calculated.
I'll tell you the approach i used to tackle this issue.
Used an array to store heights (using sizeWithFont). It is deprecated now, so you should try using sizeWithAttributes instead.
Log the height from the array, and the height of the actual label, and see if they match.
Make sure the UIFont used is the same in sizeWithFont, and yourLabel.font.
Check to see if you have taken any kind of padding in the cells, like leaving pixels on the top of the cell.
These are the general tips.
When you create a cell in tableView: cellForRowAtIndexPath: you can store it into a NSMutableArray
Then when tableView: heightForRowAtIndexPath: simply retrieve the cell and specify it height.
It's how I do it most of the time but if you use a lot of cell it might use to much memory.
Or maybe you can only store the size of the cell in the NSMutableArray.
EDIT:
I'm not sure I understand you correctly.
You want your cell to be able to contain you wrapper?
cell.contentView.frame = CGRectMake(0, 0, cell.contentView.frame.size.width, wrapper.frame.origin.y + wrapper.frame.size.height);
Store the cell in an NSMutableArray in tableView: cellForRowAtIndexPath:, retrieve it based on it index in tableView: heightForRowAtIndexPath:
To calculate your Label height you should consider different things, You have to calculate size by keeping text length and its font in mind and the bounds of frame you are adding in. Below is example how to calculate size for dynamic text.
CGSize boundingSize = CGSizeMake(frame.size.width, CGFLOAT_MAX);
CGSize requiredSize = [text sizeWithFont:yourFont constrainedToSize:boundingSize lineBreakMode:NSLineBreakByWordWrapping];
I have a label whose text is longer that the table view's header containing it, so I want the label to be split into N lines according to the width available. This is my code:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
if (section == 1) {
UIView *wrapper = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 100)];
[wrapper setBackgroundColor:[UIColor clearColor]];
UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 100)];
textLabel.text = NSLocalizedString(#"This is supposed to be a very long text that may fill several lines", #"");
[textLabel setLineBreakMode:UILineBreakModeWordWrap];
[textLabel setNumberOfLines:0];
[wrapper addSubview:textLabel];
return wrapper;
}
else
return nil;
}
However, the label is in a single line and I can't see the end of the text. What am I missing?
Thanks!
You should implement the delegate method heightForHeaderInSection.
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if(section ==1)
return 100;
else
return 0;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
if (section == 1) {
NSString *text = NSLocalizedString(#"This is supposed to be a very long text that may fill several lines", #"");;
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:10] constrainedToSize:CGSizeMake(self.tableView.frame.size.width, 999) lineBreakMode:NSLineBreakByWordWrapping];
CGRect frame = CGRectZero;
frame.size = size;
UIView *wrapper = [[UIView alloc] initWithFrame:frame];
[wrapper setBackgroundColor:[UIColor clearColor]];
UILabel *textLabel = [[UILabel alloc] initWithFrame:frame];
textLabel.text = NSLocalizedString(#"This is supposed to be a very long text that may fill several lines", #"");
[textLabel setLineBreakMode:UILineBreakModeWordWrap];
[textLabel setNumberOfLines:0];
[wrapper addSubview:textLabel];
return wrapper;
}
else
return nil;
}
use this if you want to support iOS below 6.0
textLabel.lineBreakMode = UILineBreakModeWordWrap;
textLabel.numberOfLines = 0;
As UILineBreakModeWordWrap deprecated now.
otherwise use this
textLabel.lineBreakMode = NSLineBreakByWordWrapping;
textLabel.numberOfLines = 0;
you should set size of label as text in heightForRowAtIndexPath
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *text;
CGSize textViewSize;
int width = 270;
textViewSize = [text sizeWithFont:[UIFont fontWithName:#"Helvetica Neue" size:14.0]
constrainedToSize:CGSizeMake(width, FLT_MAX)
lineBreakMode:UILineBreakModeCharacterWrap];
CGFloat height = MAX(textViewSize.height, 44.0f);
return height;
}
and in cellForRowAtIndexPath write this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
textViewSize = [text sizeWithFont:[UIFont fontWithName:#"Helvetica Neue" size:14.0]
constrainedToSize:CGSizeMake(width, FLT_MAX)
lineBreakMode:UILineBreakModeCharacterWrap];
// / CGFloat height = MAX(textViewSize.height, 44.0f);
// if (!lblParameter){
// lblParameter = (UILabel*)[cell viewWithTag:1];}
CGFloat height = MAX(textViewSize.height, 44.0f);
[lblParameter setText:text];
[lblParameter setFont:[UIFont fontWithName:#"Helvetica Neue" size:14.0]];
[lblParameter setFrame:CGRectMake(10, 0,width,height)];
[[cell contentView] addSubview:lblParameter];
}
you need caculate the height of uilabel this height equal font size * numberline
It seems that wrapping / formatting will not work on a label inside the header view - presumably because it's inheriting defaults from somewhere. So, a simple solution, which AFAIK breaks no laws, is to put the label inside a view inside the header view.
Using the storyboard, drag a view to the table view controller (above your prototype cell).
Add a second view to this view.
Add the label to this second view. Make sure both views and label are tall enough to allow for multiple lines of text. In the label's attributes, set Lines to 0 and Line Breaks to Word Wrap.
Wrapping will now work as expected.
I'm very much new to iphone development and strucked at a point. Actually my need is I'm displaying some results in a UITableView and I'm displaying then UILables of UITableView's cell..
How can I make the UILabel's to adjust the content according to the text. Actually text is not static .It may be changing at run time.So I need to set dynamic size to a UILabel .Ans secondly suppose if the text is null then it should not show any space and immediately the next UILabel should start .How can I make it possible ?
Here is my code..
self.tableView=[[UITableView alloc] initWithFrame:CGRectMake(0,150,320,800) style:UITableViewStylePlain];
self.tableView.delegate=self;
self.tableView.dataSource=self;
self.tableView.scrollEnabled = NO;
[self.view addSubview:self.tableView];
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(currentHtmlElement==#"3")
{
static NSString *CellIdentifier=#"Cell";
UITableViewCell* cell=[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]autorelease];
}
NSMutableDictionary *d = (NSMutableDictionary *) [arr2 objectAtIndex:indexPath.row];
cell.accessoryType= UITableViewCellAccessoryDetailDisclosureButton;
UILabel *name1= [[UILabel alloc]initWithFrame:CGRectMake(10, 5, 320, 40)];
name1.font=[UIFont boldSystemFontOfSize:16];
[name1 setTextAlignment:UITextAlignmentLeft];
[name1 setText:[d valueForKey:#"Name"]];
name1.tag = 111;
[cell addSubview:name1];
[name1 release];
UILabel *codetype=[[UILabel alloc]initWithFrame:CGRectMake(10, 45, 320,15)];
codetype .font=[UIFont boldSystemFontOfSize:12];
[codetype setTextAlignment:UITextAlignmentLeft];
[codetype setText:[d valueForKey:#"Code"]];
codetype.tag=112;
[cell addSubview:codetype];
[codetype release];
UILabel *id=[[UILabel alloc]initWithFrame:CGRectMake(10, 68, 320,10)];
id .font=[UIFont fontWithName:#"arial" size:12];
[id setTextAlignment:UITextAlignmentLeft];
[id setText:[d valueForKey:#"ID"]];
id.tag=113;
[cell id ];
[id release];
UILabel *address=[[UILabel alloc]initWithFrame:CGRectMake(10, 85, 320,10)];
address .font=[UIFont fontWithName:#"arial" size:12];
[address setTextAlignment:UITextAlignmentLeft];
[address setText:[d valueForKey:#"address"]];
line2.tag=114;
[cell addSubview: address];
[address release];
UILabel *city = [[UILabel alloc]initWithFrame:CGRectMake(10, 105, 320, 10)];
city .font=[UIFont fontWithName:#"arial" size:12];
[city setTextAlignment:UITextAlignmentLeft];
[city setText:[d valueForKey:#"City"]];
city.tag=115;
[cell addSubview:city ];
[city release];
}
Here I have set all the UILabels to static size.But what I need is dynamic size.and suppose if the address label is null then it should not show any space and next label i.e city should start immediately next after id ..How is it possible?
I did this task in my code.... you can use this code and do changes according to your code....and let me know if you get any problem.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
NSMutableDictionary *dicNotification =[[NSMutableDictionary alloc]initWithDictionary: [myNotification objectAtIndex:indexPath.row]];
//code to get notification text height
NSString *text = [dicNotification objectForKey:#"message"];
CGSize constraint = CGSizeMake(175, 100.0f);
CGSize size = [text sizeWithFont: [UIFont fontWithName:#"Verdana" size:12] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
return size.height;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
//label for message
UILabel *lblMsg = [[UILabel alloc] initWithFrame:CGRectZero];
lblMsg.tag = 12;
lblMsg.backgroundColor = [UIColor clearColor];
[lblMsg setFont:[UIFont fontWithName:#"Verdana" size:12]];
[lblMsg setLineBreakMode:UILineBreakModeWordWrap];
[cell addSubview:lblMsg];
}
NSMutableDictionary *dicNotification =[[NSMutableDictionary alloc]initWithDictionary: [myNotification objectAtIndex:indexPath.row]];
//get the message height set frame height to imageView(BubbleMid & Top) & labelMSG
NSString *txt = [dicNotification objectForKey:#"message"];
CGSize constraint = CGSizeMake(175, 2000.0f);
CGSize size = [txt sizeWithFont: [UIFont fontWithName:#"Verdana" size:12] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
//set frame height to labelMsg
UILabel *lblMsg = (UILabel *)[cell viewWithTag:12];
lblMsg.frame = CGRectMake(15,25, 175, size.height);
[lblMsg setNumberOfLines:size.height/16];
//set labelMsg text
lblMsg.text = [dicNotification objectForKey:#"message"];
return cell;
}
The thing is that you must use heightForRowAtIndexPath. The problem is that it is called prior to cellForRowAtIndexPath, therefore the text is not in the component and you can not simply adjust the component size to the text. So, what you do is calculate the text size using a sizeWithFont.
In case of normal/standard text
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
//here you get your text string
NSString* theText = ... ;
//then calculate the size. systemFontOfSize depends on your font size
//yourLabelWidth must be known and usually depends on scree size or if the tableview has an index
CGSize textSize = [theText sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:CGSizeMake(yourLabelWidth, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping];
return textSize.height;
}
In the case of attributed text
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
//here you get your text string
NSAttributedString* theText = ...;
//then calculate the size.
//yourLabelWidth must be known and usually depends on scree size or if the tableview has an index
CGRect rectSize = [theText boundingRectWithSize:CGSizeMake(yourLabelWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:NULL];
return rectSize.size.height;
return textSize.height;
}
you can make UILable adjust content with bellow code
CGSize sizeToMakeLabel = [name1.text sizeWithFont: name1.font];
name1.frame = CGRectMake(name1.frame.origin.x, name1.frame.origin.y,
sizeToMakeLabel.width, sizeToMakeLabel.height);
and here set second lable like bellow..
UPDATE
if([[d valueForKey:#"address"] isEquelToString:#""]){
UILabel *city=[[UILabel alloc]initWithFrame:CGRectMake(10, idLable.frame.origin.y + id.frame.size.height+10 , 320,15)];
city .font=[UIFont fontWithName:#"arial" size:12];
[city setTextAlignment:UITextAlignmentLeft];
[city setText:[d valueForKey:#"City"]];
city.tag=115;
[cell addSubview:city ];
CGSize sizeToMakeLabel = [city.text sizeWithFont: city.font];
city.frame = CGRectMake(city.frame.origin.x, city.frame.origin.y,
sizeToMakeLabel.width, sizeToMakeLabel.height);
}
else{
UILabel * address =[[UILabel alloc]initWithFrame:CGRectMake(10, idLable.frame.origin.y + id.frame.size.height+10 , 320,15)];
address .font=[UIFont fontWithName:#"arial" size:12];
[address setTextAlignment:UITextAlignmentLeft];
[address setText:[d valueForKey:#"address"]];
line2.tag=114;
[cell addSubview: address];
CGSize sizeToMakeLabel = [address.text sizeWithFont: address.font];
address.frame = CGRectMake(address.frame.origin.x, address.frame.origin.y,
sizeToMakeLabel.width, sizeToMakeLabel.height);
}
and so on for every label..
follow this logic for whole label
i hope this help you...
In my cellForRowAtIndexPath, I'm doing some custom formatting on a subview for when that cell is selected. The complete function is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UIView *left;
UIImageView *leftImage;
UILabel *label;
ArticleButton *btn;
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
if (!cell)
{
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:#"UITableViewCell"] autorelease];
left = [[[UIView alloc] initWithFrame:CGRectMake(0, 2, 155, 139)] autorelease];
leftImage = [[[UIImageView alloc] initWithFrame:CGRectMake(7,9,141,77)] autorelease];
label = [[[UILabel alloc] initWithFrame:CGRectMake(6,87,141,48)] autorelease];
btn = [[[ArticleButton alloc] initWithFrame:CGRectMake(0,2,155,139)] autorelease];
left.tag = 0;
leftImage.tag = 1;
label.tag = 2;
btn.tag = 3;
[btn addTarget:self action:#selector(selectArticle:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:left];
[cell.contentView addSubview:leftImage];
[cell.contentView addSubview:label];
[cell.contentView addSubview:btn];
}
else
{
left = (UIView*)[cell viewWithTag:0];
leftImage = (UIImageView*)[cell viewWithTag:1];
label = (UILabel*)[cell viewWithTag:2];
btn = (ArticleButton*)[cell viewWithTag:3];
}
...load *entry
NSURL *url = [NSURL URLWithString:[entry imageUrl]];
FeedEntry* selectedEntry = [detailViewController detailItem];
NSString* selectedTitle = selectedEntry.title;
if ([selectedTitle isEqualToString:entry.title])
{
[left setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"cellbackground_sel.png"]]]; <-- PROBLEM IS THIS IMAGE NEVER CHANGES
NSLog(#"selected row %#", selectedTitle);
}
else{
[left setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"cellbackground2.png"]]];
}
[left setNeedsDisplay];
[leftImage setImageWithURL:url placeholderImage:[UIImage imageNamed:#"placeholder.gif"]];
[leftImage setContentMode:UIViewContentModeScaleAspectFill];
leftImage.clipsToBounds = YES;
[label setBackgroundColor:[UIColor clearColor]];
label.textColor = [UIColor whiteColor];
label.text = [entry.title stringByAppendingString:#"\n\n"];
label.numberOfLines = 3;
label.lineBreakMode = UILineBreakModeClip;
label.font = [UIFont fontWithName:#"Arial-BoldMT" size:12];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
The problem I'm having is with this section:
if ([selectedTitle isEqualToString:entry.title])
{
[left setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"cellbackground_sel.png"]]];
NSLog(#"selected row %#", selectedTitle);
}
else{
[left setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"cellbackground2.png"]]];
}
Everything else works, but although I'm logging that it gets called, but the background of my subview to indicate that the row in question is a selected row, never changes. I've tried calling setNeedsDisplay, I've tried scrolling up and down trying to get the cells to dequeue and get recreated, it just never users the other image, even though it logs that the row being drawn was a selected row.
(Once I get this working, I need to implement the "right" section to have two "cells" in the one row and only one will be selected. That's why I'm doing it this way with subviews in the cell).
What am I doing wrong?
I think it is a problem to set the tag to zero. The documentation says:
viewWithTag:
Return Value
The view in the receiver’s hierarchy whose tag property matches the value in the tag parameter.
Discussion
This method searches the current view and all of its subviews for the specified view.
If think that most views tag is zero, even that of the current cell's view. I think that you dont get the correct view out of it. Try not to use 0 as a tag to work with.
Whenever a particular UITableViewCell is selected, this particular delegate is called -
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Try to see if your button taps are being first responded by this delegate rather than going to selectArticle: selector you have defined...
Instead of setting this in tableview:cellForRowAtIndexPath:, try doing it in the tableview:willSelectRowAtIndexPath:
See the protocol reference for further details.
I have a UILabel for each cell at cellForRowAtIndexPath.
UILabel *cellLabel = [[UILabel alloc] initWithFrame:frame];
cellLabel.text = myString;
I want to access that string "myString" at didSelectRowAtIndexPath using indexpath.
NSString *anotherString = cell.textLabel.text;
returns null.
Now if at cellForRowAtIndexPath, I did something like cell.textLabel.text = theString; then the didSelectRowAtIndexPath returns the appropriate cell.
My question is, how can I access the text in the UILabel I apply to the cell, at didSelectRowAtIndexPath?
Also, logging the cell in didSelectRowAtIndexPath returns cell: <UITableViewCell: 0x5dcb9d0; frame = (0 44; 320 44); autoresize = W; layer = <CALayer: 0x5dbe670>>
Edit:
NSString *myString = [[results objectAtIndex:indexPath.row] valueForKey:#"name"];
//cell.textLabel.text = myString;
CGFloat width = [UIScreen mainScreen].bounds.size.width - 50;
CGFloat height = 20;
CGRect frame = CGRectMake(10.0f, 10.0f, width, height);
UILabel *cellLabel = [[UILabel alloc] initWithFrame:frame];
cellLabel.text = myString;
cellLabel.textColor = [UIColor blackColor];
cellLabel.backgroundColor = [UIColor whiteColor];
cellLabel.textAlignment = UITextAlignmentLeft;
cellLabel.font = [UIFont systemFontOfSize:14.0f];
[cell.contentView addSubview:cellLabel];
[cellLabel release];
return cell;
In this line of code:
NSString *anotherString = cell.textLabel.text;
How are you obtaining the cell? Is it nil? Also, the textLabel field you're accessing is the default label in the a UITableViewCell and not the label you are adding in -cellForRowAtIndexPath. Here is how you can get the cell from -didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tv
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self tableView:tv cellForRowAtIndexPath:indexPath];
}
The issue at this point, though, is that you can't access the UILabel by name, however, you can access it if you've set a tag. So, when you create your UILabel, set the tag like this:
UILabel *cellLabel = [[UILabel alloc] initWithFrame:frame];
cellLabel.text = myString;
cellLabel.textColor = [UIColor blackColor];
cellLabel.backgroundColor = [UIColor whiteColor];
cellLabel.textAlignment = UITextAlignmentLeft;
cellLabel.font = [UIFont systemFontOfSize:14.0f];
// Set the tag to any integer you want
cellLabel.tag = 100;
[cell.contentView addSubview:cellLabel];
[cellLabel release];
So, now you can access the UILabel by tag:
- (void)tableView:(UITableView *)tv
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self tableView:tv cellForRowAtIndexPath:indexPath];
UILabel *label = (UILabel*)[cell viewWithTag:100];
NSLog(#"Label Text: %#", [label text]);
}
Can you post the code that you assign the cellLabel to the cell? Did you do something like this: cell.textLabel = cellLabel?
Look for UITableViewCell for more details