Label in UITableViewCell subclass changes position - iphone

I have made a prototype cell in Interface Builder. This prototype contains an imageview which should change size depending on the length of a string.
Therefore I have written a method in my subclass of UITableViewCell to calculate the width of the text string with a given font and set this width to the width of the imageview.
This works fine until I push to another view controller and then go back to my table view. Then the view has been repositioned about 20 pixels to the right.
Does anyone know why this happens? This even happens if I set the x-position to something static, e.g. 200.
Update 1:
In my - (UITableViewCell *)tableView:cellForRowAtIndexPath: I use my subclass of UITableViewCell, DFolderCell. This calls the method - (void)updateIndicatorsCount:andStarred which sets the value of the count and the starred label and makes those labels and the images countIcon and starredIcon the width to the width of _count and _starred with a given font.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"folderCell";
DFolderCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[DFolderCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
int index = indexPath.row;
Folder *folder = (Folder *) [folders objectAtIndex:index];
FolderIcon icon = (FolderIcon) folder.icon.intValue;
cell.name.text = folder.name;
cell.icon.image = [[DFolders sharedFolders] imageForFolderIcon:icon];
int count = [[DDrafts sharedDrafts] amountOfDraftsInFolder:folder withType:DraftLoadTypeRecent];
int starred = [[DDrafts sharedDrafts] amountOfDraftsInFolder:folder withType:DraftLoadTypeStarred];
[cell updateIndicatorsCount:count andStarred:starred];
return cell;
}
The method below is part of my subclass DFolderCell
/* Update size of count and starred indicators */
- (void)updateIndicatorsCount:(int)_count andStarred:(int)_starred
{
UIFont *badgeFont = [UIFont systemFontOfSize:11.0];
UIImage *background;
_count = 1000; // Test
if (_count > 0)
{
self.count.text = [NSString stringWithFormat:#"%i", _count];
CGSize size = [self.count.text sizeWithFont:badgeFont];
CGRect frame = self.countIcon.frame;
frame.size.width = size.width;
frame.origin.x = 320 - 20 - size.width;
self.countIcon.frame = frame;
self.count.frame = frame;
background = [UIImage imageNamed:#"Folders_Badge_Count.png"];
self.countIcon.image = [background stretchableImageWithLeftCapWidth:2 topCapHeight:2];
self.count.hidden = NO;
self.countIcon.hidden = NO;
}
/* Code for _starred is similar to code for _count */
}
This is the result.
This is how it should always look (and how it looks before selecting a cell)
When a cell has been selected, a new viewcontroller has been pushed onto the navigation stack and I have popped this view controller by going back, this is how it looks.
Update 2:
I have kept the imageview in the cell prototype but commented out the code setting its size and its image to a stretchable image. I have also changed the background color of the label to a solid red to see the exact size of the label. The size seems to be correct.
This is my - (void)updateIndicatorsCount:andStarred: method now.
- (void)updateIndicatorsCount:(int)_count andStarred:(int)_starred
{
UIFont *badgeFont = [UIFont systemFontOfSize:11.0];
// UIImage *background;
_count = 1000;
if (_count > 0)
{
self.count.text = [NSString stringWithFormat:#"%i", _count];
CGSize size = [self.count.text sizeWithFont:badgeFont];
CGRect frame = self.countIcon.frame;
frame.size.width = size.width;
frame.origin.x = 320 - 30 - size.width;
self.count.frame = frame;
self.count.hidden = NO;
self.count.backgroundColor = [UIColor redColor];
/* DON'T DO ANYTHING TO THE IMAGE */
/*
background = [UIImage imageNamed:#"Folders_Badge_Count.png"];
self.countIcon.hidden = NO;
self.countIcon.image = [background stretchableImageWithLeftCapWidth:2 topCapHeight:2];
self.countIcon.frame = frame;
*/
}
}
Before pushing to another view controller:
After going back from the view controller:

Use this code in your cellForRowatindexpath it will solve your issue
[[[cell contentView] subviews] makeObjectsPerformSelector: #selector(removeFromSuperview)];

Related

UITableView display

I add UIView in UITableviewcell. The first occurrence of all appears normal. But every time when you scroll all the strays. UILabel overlap with different values. Below is the code to create Cell. What am I doing wrong?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
float low_bound = 0;
float high_bound = 7;
float rndValue = (((float)arc4random()/0x100000000)*(high_bound-low_bound)+low_bound);
int dayStart = rndValue;
int dayMonth = 30;
int maxI = (dayStart+(7-dayStart))*(indexPath.row+1);
int Pos;
if (indexPath.row == 0) {
dayNumber = dayStart;
Pos = 0;
} else {
Pos = dayNumber;
}
for (int i=dayNumber; i < maxI; i++) {
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(4, 4, 40, 40)];
if (i > 0) {
CGRect frame = myView.frame;
frame.origin.x = (44.0*(i- Pos-1));
myView.frame = frame;
}
UILabel *yourLabel = [[UILabel alloc] initWithFrame:CGRectMake(24, 0, 40, 40)];
[yourLabel setText:[NSString stringWithFormat:#"%i", i-dayStart+1]];
[myView addSubview:yourLabel];
[cell addSubview:myView];
dayNumber++;
}
return cell;
}
THe cells you are using are being reused by the system, and every tieme that happens, you just slap another label on top of all the others. You need to either remove the labels before reusing the cell by doing something like
//this needs to be placd before the for-loop
for (UIView *view in cell.contentView.subViews){
[view removeFromSuperview];
}
or you can keep references to the labels and just assign new values to them.
I would advise you to do the latter, but it will probably mean that you have to subclass UITableViewCell.
Right now, the code in your forloop keeps creating new labels and just stacks them up like a lasagna.

UILabel doesn't change height on setFrame

I'm trying to change the height of a UILabel depending on how much text is in the label.
I can calculate the size required for the label but when I try to set the UILabel frame it just doesn't change.
Below is my code.
Even if I replace size.height in the last line to something like 500 the size of the UILabel frame doesn't change
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"GameItemCell";
GameItemCell *cell = (GameItemCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"GameItemCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
GameItem* item = [_hunt.gameItems objectAtIndex: indexPath.row];
cell.itemHeaderLabel.text = [NSString stringWithFormat:#"#%d - (%d pts)", indexPath.row+1, item.itemPoints];
UILabel* textLabel = cell.itemTextLabel;
textLabel.text = item.itemText;
textLabel.lineBreakMode = NSLineBreakByWordWrapping;
CGRect frame = cell.itemTextLabel.frame;
CGSize textSize = { frame.size.width, 20000.0f };
CGSize sizeOneLine = [#"one line" sizeWithFont:cell.itemTextLabel.font constrainedToSize:textSize lineBreakMode:NSLineBreakByWordWrapping];
CGSize cellTextSize = [item.itemText sizeWithFont:cell.itemTextLabel.font constrainedToSize:textSize lineBreakMode:NSLineBreakByWordWrapping];
CGSize sizeOneLineSpacing = CGSizeMake(sizeOneLine.width, sizeOneLine.height + 3);
NSInteger lines = cellTextSize.height / sizeOneLine.height;
CGSize size = CGSizeMake(frame.size.width, lines * sizeOneLineSpacing.height);
textLabel.frame = CGRectMake(frame.origin.x, frame.origin.y, size.width, size.height);
return cell;
}
You must set the frame of your label in GameItemCell inside -(void)layoutSubviews
Instead of doing all that hard work, try it like:
textLabel.numberOfLines = 0;
textLabel.text = textString;
[textLabel sizeToFit];
remember sizeToFit respect your labels default width, so set width according to your requirement. And then Height will be managed by the sizeToFit method.
In the end you will need to put something like these two methods in your UITableViewCell subclass:
// call this method on your cell, during cellForRowAtIndexPath
// provide your resizing info (frame, height, whatever)
- (void) updateLabelFrame:(CGRect)newLabelFrame {
self.resizedLabelFrame = newLabelFrame;
[self setNeedsLayout];
}
// the actual resize happens here when UIKit gets around to it
- (void) layoutSubviews {
[super layoutSubviews];
self.myLabel.frame = self.resizedLabelFrame;
}
You have missed numberOfLines property to set.
Add :
textLabel.numberOfLines = 0;
CGRect labelFrame = myLabel.frame;
labelFrame.size = [myLabel.text sizeWithFont:myLabel.font constrainedToSize:CGSizeMake(myLabel.frame.size.width, CGFLOAT_MAX)
lineBreakMode:myLabel.lineBreakMode];
cell.textLabel.frame = labelFrame;
numberOfLines
The maximum number of lines to use for rendering text.
The default value for this property is 1. To remove any maximum limit, and use as many lines as needed, set the value of this property to 0.
tack a look UILabel Class Reference
According to your question you can set no of lines
Now number of lines is not set to your label, so set it,
textLabel.numberOfLines = lines;
Yeah, for multiple lines use textLabel.numberOfLines = 0; in cellForRowAtIndexPath
But Still you need to change the height of the cell too:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Calculate new cell height too as you are doing in cellForRowAtIndexPath
return YOUR_SIZE;;
}
Try this :
NSString *text1 = shareObjC.commentText;
CGSize constraint1 = CGSizeMake(280 - (size.width + 5), 2000);
CGSize size1 = [text1 sizeWithFont:[UIFont systemFontOfSize:12] constrainedToSize:constraint1 lineBreakMode:UILineBreakModeWordWrap];
UILabel *lblComment = [[[UILabel alloc] initWithFrame:CGRectMake(posx,posy,size1.width,size1.height)] autorelease];
lblComment.lineBreakMode = UILineBreakModeWordWrap;
lblComment.numberOfLines = size1.height/15;
[lblComment setFont:[UIFont systemFontOfSize:12]];
lblComment.text = text1;
[cell.viewLikeComment addSubview:lblComment];

UIScrollView in custom table cell at cellForRowAtIndex path not working

I'm trying to implement a UIScrollView in each cell of `UITableView'. I wrote my code based on Apple's custom Scrollview project: https://developer.apple.com/library/ios/#samplecode/Scrolling/Listings/ReadMe_txt.html
Each table cell's UIScrollView scrolls through a set of labels, which are initialized in the cellForRowAtIndexPath method. Each label's text is set after initialization to equal a string from an array.
Everything works correctly until I scroll down to the 4th table cell. The UIScrollView in this cell has the same exact labels as the first cell. The same first 3 set of labels or the first 3 scrollViews keep repeating after the first 3 cells. The strange part is, when I log the label.text after it is set, the output shows the correct label.text of what should be displaying in the respective cell.
- (void)layoutScrollLabels: (float)arrayCount
{
UIView *view = nil;
NSArray *subviews = [cell.popularLinkScrollView subviews];
// reposition all image subviews in a horizontal serial fashion
CGFloat curXLoc = 0;
for (view in subviews)
{
if ([view isKindOfClass:[UILabel class]] && view.tag >= 0)
{
CGRect frame = view.frame;
frame.origin = CGPointMake(curXLoc, 0);
view.frame = frame;
curXLoc += (kScrollObjWidth);
}
}
[cell.popularLinkScrollView setContentSize:CGSizeMake((arrayCount * kScrollObjWidth), [cell.popularLinkScrollView bounds].size.height)];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Custom Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *dictionary = [parseDataArray objectAtIndex:indexPath.row];
NSArray *popularLinkTitleArray = [dictionary objectForKey:#"popularLinkTitleArray"];
cell.popularLinkScrollView.clipsToBounds = YES;
kScrollObjHeight = cell.popularLinkScrollView.frame.size.height;
kScrollObjWidth = cell.popularLinkScrollView.frame.size.width;
NSUInteger i;
for (i = 0; i < popularLinkTitleArray.count; i++)
{
NSString *string = [NSString stringWithFormat:#"%#", [popularLinkTitleArray objectAtIndex: i]];
UILabel *label = [[UILabel alloc] init];
label.text = [NSString stringWithFormat:#"%#", string];
label.backgroundColor = [UIColor clearColor];
label.numberOfLines = 5;
NSLog(#"%#", label.text);
// setup each frame to a default height and width, it will be properly placed when we call "updateScrollList"
CGRect rect = label.frame;
rect.size.height = kScrollObjHeight;
rect.size.width = kScrollObjWidth;
label.frame = rect;
label.tag = i; // tag our images for later use when we place them in serial fashion
[cell.popularLinkScrollView addSubview:label];
}
[self layoutScrollLabels:popularLinkTitleArray.count];
}
return cell;
}
You should not add label as a subview to popularLinkScrollView in cellForRowAtIndexPath. If the count of labels is required to be dynamic, try this code before your for loop in cellForRowAtIndexPath.
for (UIView* subView in cell.popularLinkScrollView.subviews)
[subView removeFromSuperview];

Dynamically change UITextView height instead of scroll

I'm trying to create a simple Diary app. In it I have a ShowPostTableViewController where I want to display each post. In my tableView I have one section with two cells, one for the headline and one for the post's body. The headline is inside an UITextField and the body is inside a UITextView. I declare both like this:
UITextField * postHeadline;
UITextView * postText;
And i synthesize them in my implementation file. I setup the tableView's cells in the willDisplayCell method. It looks like this:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath {
tableView.allowsSelection = NO;
CGRect wholeWindow = [self.view bounds];
float headlineHeight = 40;
CGRect headlineRect = CGRectMake(10, 10, wholeWindow.size.width, headlineHeight);
CGRect bodyRect = CGRectMake(wholeWindow.origin.y, wholeWindow.origin.x,
wholeWindow.size.width, wholeWindow.size.height);
postHeadline = [[UITextField alloc] initWithFrame:headlineRect];
postText = [[UITextView alloc] initWithFrame:bodyRect];
NSString * headline = [[NSString alloc] initWithFormat:#"%#",[currentPostArray
objectAtIndex:0]];
NSString * body = [[NSString alloc] initWithFormat:#"%#",[currentPostArray
objectAtIndex:1]];
postHeadline.text = headline;
postHeadline.font = [UIFont fontWithName:#"Helvetica-Bold" size:24.0];
postText.text = body;
postText.backgroundColor = [UIColor colorWithRed:254.0/255.0 green:255.0/255.0
blue:237.0/255.0 alpha:1];
postText.font = [UIFont fontWithName:#"Georgia" size:17.0];
postText.scrollEnabled = NO;
postText.delegate = self;
if (indexPath.row == 0) {
[cell.contentView addSubview:postHeadline];
}
if (indexPath.row == 1) {
[cell.contentView addSubview:postText];
CGRect frame = postText.frame;
frame.size.height = postText.contentSize.height;
postText.frame = frame;
}
}
Yep, I'm a beginner, probably leaking memory like crazy. A question related to that. It seems like my UITextField postHeadline gets set twice, I can't erase it because it's two layers of the same text. How can I solve that?
Back to my original question. My cellForRowAtIndexPath is left petty much intact. I have sat up the delegate methods for my UITextView (think it's here the problem lies). I found a solution that almost worked here: UITextView change height instead of scroll.
They look like this:
- (void)textViewDidBeginEditing:(UITextView *)textView
{
textView.frame =CGRectMake(textView.frame.origin.x,textView.frame.origin.y,textView.
frame.size.width,textView.frame.size.height + 100);
}
This method should resize the textView:
- (void)textViewDidChange:(UITextView *)textView
{
CGFloat fontHeight = (textView.font.ascender - textView.font.descender) + 1;
CGRect newTextFrame = textView.frame;
newTextFrame.size = textView.contentSize;
newTextFrame.size.height = newTextFrame.size.height + fontHeight;
textView.frame = newTextFrame;
}
I set the row height like this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath
*)indexPath
{
NSString * body = [[NSString alloc] initWithFormat:#"%#",[currentPostArray
objectAtIndex:1]];
CGSize bodySize = [body sizeWithFont:[UIFont fontWithName:#"Georgia" size:17.0]
constrainedToSize:CGSizeMake(self.view.frame.size.width,CGFLOAT_MAX)];
if (indexPath.section == 0 && indexPath.row == 1) {
return bodySize.height + 100;
}
return 50;
}
The thing almost works. When the user hits return, the textview seems to expand, but it only works 14 times, then the cursor hides behind the keyboard.
Any ideas and tips how to solve this would be great!
Thanks
// Anders
EDIT
I solved the "headline gets set twice" problem by moving most of my willDisplayCell method code to my viewDidLoad method. And I think I have localized where the rezising problem is. I think it is in my heightForRowAtIndexPath method. It currently looks like this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath
*)indexPath
{
NSString * body = [[NSString alloc] initWithFormat:#"%#",[currentPostArray
objectAtIndex:1]];
CGSize bodySize = [body sizeWithFont:[UIFont fontWithName:#"Georgia" size:17.0]
constrainedToSize:CGSizeMake(self.view.frame.size.width,CGFLOAT_MAX)];
if (indexPath.section == 0 && indexPath.row == 1) {
return bodySize.height + 100;
}
return 50;
}
Since array and it's content gets set in the viewDidLoad method, the size stays the same. I therefore think I need to dynamically increase the cell's size after my TextView's content too. Because when I write like this:
if (indexPath.section == 0 && indexPath.row == 1) {
return 1000;
}
The cell's size increases and the user can hit "return" more times before the keyboard gets in the the way. Is there a way to increase to cells's height dynamically?
Think I solved it. These methods did the magic trick:
[self.tableView beginUpdates];
[self.tableView endUpdates];
I updated my heightForRowAtIndexPath so it looks like this:
if (indexPath.section == 0 && indexPath.row == 1) {
return postText.frame.size.height + headlineHeight;
}
return 44;
And I added these methods to viewDidChange and in viewWillAppear (to reset the sizes).
[self.tableView beginUpdates];
[self.tableView endUpdates];
That was pretty much it.

UITableViewCells' labels with different heights alter labels' widths

In my UITableViewCells I am displaying text of varying lengths. To accomodate larger amounts of text and while also not requiring small texts amounts to be in huge table cells I am setting the height of the rows here...
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
float padding = 40.0f;
CGSize constraintSize;
constraintSize.width = 320.0f - padding - padding;
constraintSize.height = MAXFLOAT;
CGSize theSize = [thisrowstext sizeWithFont:[UIFont systemFontOfSize:14.0f]
constrainedToSize:constraintSize
lineBreakMode:UILineBreakModeWordWrap];
if(theSize.height < 24.0f) return 44.0f;
else return theSize.height + 20.0f;
}
...it works well, unfortunately the textLabel's width seems to be affected too with some textLAbels (depending on row count) being pushed in a few pixels. I've tried setting indentation values, but that doesn't work. Has anyone else encountered this?
EDIT: I'm adding the layoutSubviews method of my UITableViewCell subclass that i'm using (no NIB).
- (void)layoutSubviews
{
[super layoutSubviews];
if (self.hideImage)
{
self.imageView.alpha = 0.0f;
self.imageView.frame = CGRectMake(-40.0f, 1.0f, 40.0f, 40.0f);
CGRect frame = self.textLabel.frame;
self.textLabel.frame = CGRectMake(frame.origin.x - 40.0f, frame.origin.y, frame.size.width + 40.0f, frame.size.height);
[self.textLabel setNeedsLayout];
}
else
{
self.imageView.alpha = 1.0f;
self.imageView.frame = CGRectMake(1.0f, 1.0f, 40.0f, 40.0f);
[self.textLabel setNeedsLayout];
}
}
EDIT: Also adding cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *TableCellViewWithHidableImageIdentifier = #"TableCellViewWithHidableImage";
TableCellViewWithHidableImage *cell = (TableCellViewWithHidableImage *)[tableView dequeueReusableCellWithIdentifier:TableCellViewWithHidableImageIdentifier];
if (cell == nil)
{
cell = [[[TableCellViewWithHidableImage alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TableCellViewWithHidableImageIdentifier] autorelease];
}
cell.hideImage = NO;
cell.imageView.image = [UIImage imageNamed:#"empty.png"];
cell.textLabel.font = [UIFont systemFontOfSize:16.0f];
cell.textLabel.numberOfLines = 0;
cell.textLabel.text = #"whatever";
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
It's hard to tell without seeing how you generate your table cells. Do you use a nib for your cells? I've found it much easier to use a nib for custom table cells, so try that if you aren't already.
I suspect you may have a problem with your autoresizeMask on one or more of your table cells' subviews.
This is a bit of a guess, but it looks to me like it is the imageView on the left hand side of the cell. Seems like as the cell gets taller, it gets wider (trying to maintain aspect ratio?) and this is pushing your text to the right. What's odd is you image is not stretched. Might be worthwhile looking at what is happening to that image view during layoutSubviews. If your custom cell does not implement this method, maybe the base class' implementation is doing something you don't expect. You could override it and NSLog to the frame of the image view before and after the a call to [super layoutSubviews] to see whats going on.