UITableViewCell with UILabel subview caching issue - objective-c

First of all, please don't tell me this is a duplicate. I know this question has been asked and answered many times but I still can't seem to get my code to work even after reading everyone else's solutions.
I'm having an issue with my UITableViewCell that contains a UILabel subview. The UILabel sometimes doesn't appear in certain cells until I scroll away from those cells and return to them. Here is the code I am using to customize the cells:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UILabel *label;
if (cell == nil) {
// cell is nil, create it
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 50, 33)];
label.tag = 888;
} else {
label = (UILabel*)[cell.contentView viewWithTag:888];
[label removeFromSuperview];
}
label.text = #"Label Text";
label.backgroundColor = [UIColor clearColor];
[label sizeToFit];
label.center = CGPointMake(cell.contentView.frame.size.width-label.frame.size.width/2-20, cell.contentView.frame.size.height/2);
[cell.contentView addSubview:label];
// customize cell text label
cell.textLabel.text = #"Cell Text";
cell.textLabel.textColor = [UIColor darkGrayColor];
return cell;
}
It appears as though the label shows up correctly when dequeueReusableCellWithIdentifier:CellIdentifier returns a non nil value but does not show up if the return value is nil and a new cell must be instantiated.
If anyone has an idea of why this might be happening, help would be greatly appreciated.

I see a couple of things you want to do.
1) read up on "sizeToFit" - the description says if the view has no superview you may get weird results.
2) When you create the view, add it to the cell immediately.
3) After you resize the cell, get its size, then compute the proper frame - I'd suggest not using "center" but I do not know that your code won't work for a fact.
4) Before even changing the center to changing the frame, hard code something like this:
CGRect r = label.frame;
r.origin = (CGPoint){ 20, 20 };
label.frame = r;
This will at least convince you that new cells and old cells are working properly. Then you can compute the frame you really want, or further play with center.

Not sure of the cause of your problem, but there are some improvements you could make. Maybe one of them fixes the issue:
In the dequeu scenario, you remove the label from the view, only to add it back. Instead, you should leave it in the view hierarchy.
To avoid having to resize and move the label all the time. Why not make it sufficiently wide, set the text right aligned. That way, you don't have to resize of move the label in the dequeue scenario.

It seems that the issue may be with modifying cell.textLabel. Other posts on this site have suggested that any time this label is modified, a new label is actually created and added to the cell's contentview (as opposed to just modifying the existing label). Setting cell.textLabel.backgroundColor = [UIColor clearColor]; seems to have fixed the problem
I'm still a bit confused about this though because even adding my custom label subview last (after setting properties for cell.textLabel) didn't fix the problem - the background color of cell.textLabel had to be set to transparent/clear.

There are three things out of place:
if (cell == nil) { // cell is nil, create it
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 50, 33)];
label.tag = 888;
[cell.contentView addSubview:label]; // (1) DO THIS HERE
} else {
label = (UILabel*)[cell.contentView viewWithTag:888];
// (2) DON'T DO THIS: [label removeFromSuperview];
}
label.text = #"Label Text";
label.backgroundColor = [UIColor clearColor];
[label sizeToFit];
label.center = CGPointMake(cell.contentView.frame.size.width-label.frame.size.width/2-20, cell.contentView.frame.size.height/2);
// (3) DON'T DO THIS HERE: [cell.contentView addSubview:label];
....
I assume ARC is on, otherwise you need a [label release] after adding it to the contentView.

Related

Add subview to UITableViewCell on selection

I'm trying to add a UIView that is supposed to strikethrough the text (don't worry about the horizontal misplacement).
However, when selecting a row, the line is added several rows below. Why?
Here's my code:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%#", indexPath);
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UILabel *label = cell.textLabel;
CGSize textSize = [[label text] sizeWithAttributes:#{NSFontAttributeName:[label font]}];
CGFloat strikeWidth = textSize.width;
UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(self.view.bounds.size.height / 2, 200, strikeWidth, 2)];
lineView.backgroundColor = [UIColor redColor];
lineView.tag = 100;
[cell.contentView addSubview:lineView];
}
Instead of using a UIView and adding a subview to the cell, you should use an NSAttributedString for the cell text and the NSStrikethroughStyleAttributeName to strike through with an NSStrikethroughColorAttributeName for the strikethrough colour.
Your problem is here:
UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(self.view.bounds.size.height / 2, 200, strikeWidth, 2)];
In this case, "self" is the tableViewController, not the label or the cell. What you're doing is setting the x origin of the view as half the height of the screen, the y origin down 200 points, with a width of strikeWidth and a height of 2.
Because the line view you are adding is going to be a subview of the cell, you always want to make the frame relative to it's superview, which in this case is the cell itself. You likely want to use something like similar to below:
CGRectMake(CGRectGetMinX(cell.textLabel.frame), CGRectGetHeight(cell.contentView.frame) / 2, strikeWidth, 2)
You'll likely want to tweak values to make it line up, but you get the idea...
EDIT: Better frame added and here's more code that does it nicely:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UILabel *label = cell.textLabel;
CGSize textSize = [[label text] sizeWithAttributes:#{NSFontAttributeName:[label font]}];
CGFloat strikeWidth = textSize.width;
UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(CGRectGetMinX(label.frame), CGRectGetHeight(cell.contentView.frame) / 2, strikeWidth, 2)];
lineView.backgroundColor = [UIColor redColor];
[cell.contentView addSubview:lineView];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
I think your line is appearing outside of your cell because you set an y origin of 200 in your frame, which seems pretty high.
Moreover, if you want to play with strikethrough in a UITableviewCell, you'd better not do it this way, because on multiple selection this 'strikethroughView' will be added multiple times, and never removed. Also on a tableview reloadData, or scrolling the cells are reused, and you don't want to see these strikethroughViews randomly displayed.
Here are two ways to do it properly :
Use the NSAttributedString framework. Basically if allows you to do all sorts of things to a string, like setting color, background color, paragraph style, but also strikeThrough.
Here is what i would write in the didSelect delegate method :
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSString *string = cell.textLabel.text;
NSDictionary *attributes = #{NSStrikethroughStyleAttributeName: #(NSUnderlineStyleThick)};
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string attributes:attributes];
cell.textLabel.attributedText = attributedString;
}
The other solution would be to add a Category on UICollectionViewCell and implement a "setStrikeTrough" method in it.
Hope it'll help.

creating label on uitablview cell in iphone?

I am new to iPhone devlopemnt I am developing one app. In that app I need show list items with their respective prices in an UITableView. To solve this problem I am following some concept like creating label dynamically in UITableViewCell to show the items and their prices.
In app the first indexid == 1 means that I am showing list places on click of particular cell on list am geting list of favorite items with prices that means indexid = 2 ...on click of back button indexid = 1 then i need hide that label ...but label is not hideing it show list prices not list of items
lblText.text = nil;
btnBack.hidden = FALSE;
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.textLabel.font = [UIFont boldSystemFontOfSize:14.0];
cell.textLabel.highlightedTextColor = [UIColor orangeColor];
}
///////////////////// Cell Title /////////////////////
if (indexId == 1)
{
// NSLog(#"%#",lblText1);
cell.textLabel.text = [NSString stringWithFormat:#"%#", [test.arrTitle objectAtIndex:indexPath.row]];
lblText = [[UILabel alloc] initWithFrame:CGRectMake(250, 7, 40, 30)]; // For right alignment
lblText.text = [test.arrId objectAtIndex:indexPath.row];
[lblText setTextAlignment:UITextAlignmentCenter];
lblText.textColor = [UIColor redColor];
lblText.backgroundColor = [UIColor clearColor];
[cell addSubview:lblText];
cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"products-category-bg.png"]];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[lblText release];
}
else
{
lblText.hidden = YES;
cell.textLabel.text = [NSString stringWithFormat:#" %#", [test.arrTitle objectAtIndex:indexPath.row]];
cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"product-bg-hover.png"]];
}
return cell;
}
I have the following problem. The label is creating ok, but after on click of back button, I get label values in main tableview.
How can I release the label object with data ?
thanks & regards
Please help me out of this problem
Your code looks fine to me, just replace one line in above code with the link written below
[cell.contentView addSubview:lblText];
Instead
[cell addSubview:lblText];
Hope It will resolve your issue.
If you're using the same table view instance for the different states in your application you're also re-using cells that were created previously:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
With each cell, when the indexid == 1 you're also doing this:
[cell addSubview:lblText];
To me, this says that each cell you've created (and that will be re-used) has a UILabel added as a subview. To truly re-use that cell you need to remove that UILabel before your logic says that you need to add it again. Not only will this remove the cells when you don't need them, it will also prevent you from adding extra UILabel instances to the cell at runtime. Try adding the code below before you check indexid
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.textLabel.font = [UIFont boldSystemFontOfSize:14.0];
cell.textLabel.highlightedTextColor = [UIColor orangeColor];
}
// Be sure to remove the UILabels on the re-used cell.
for(UIView *view in cell.subviews)
if ([view isKindOfClass:([UILabel class])])
[view removeFromSuperview];
if (indexid == 1) { ....
This will remove the UILabel each time the cell is created allowing you to conditionally re-added it as a subview.
Try to set a tag to your label(will help you to set data to labels, you can get the label by calling
[cell viewWithTag:theLabelTag]
where theLabelTag tag can be the index)
and set the label value in
tableView: willDisplayCell: forRowAtIndexPath:
instead of
tableView: cellForRowAtIndexPath:

UIButton showing multiple times on UITableView when created dynamically in UITableViewCell

After trying various options, and reading numerous answers on SO, I am posting this question hoping somebody can provide some insight or a solution to this problem:
iOS5.1, XCode 4.4.1, Storyboard, ARC
I have a "Grouped" tableview, with each cell containing bunch of labels, images and buttons. One of the label, Description label, for it the text can be large or small, which means if text is large I have to increase the size of the label accordingly and place the buttons below accordingly as well. I am able to do all this, but the problem is that buttons appears twice when I scroll past the first cell and keeps repeating as I scroll.
Here's the code, all I am doing is calculating the height of label based on text, resizing the label, and then placing the 3 buttons below the description label accordingly.
The code below is just for one button but this should give the idea of what i am doing. I have tried to do similar stuff in "willDisplayCell", in the custom cell class but still the 3 buttons keeps repeating when i scroll down in the tableview. PLease refer the screenshot, the first three buttons shouldn't even show.
I noticed that position of first 3 buttons is same as if it ignores the sizeIncrement, meaning "359.0f + sizeIncrement +20" minus the "sizeIncrement", "sizeIncrement"= the height of description label after calculation
I also noticed that if i do
float y = 359.0f + 20;
instead of this
float y = 359.0f + sizeIncrement +20;
the repetition of Buttons problem is gone.
P.S: I know this is not the best code, but I have been trying lot of stuff to solve the problem so not bothered with best practices etc at this point.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"tipsid";
TipsCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[TipsCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
id tip = [_tips objectAtIndex: [indexPath section]];
NSDictionary *tipForIndex = [_tips objectAtIndex: [indexPath section]];
float sizeIncrement = [Utility getLabelHeightForText:[tipForIndex objectForKey:#"tipDescripion"]
withWidth:285.0f
withFont:[UIFont systemFontOfSize:14.0f]];
// Here I Resize the Label, code below to create a button and position accordingly.
float y = 359.0f + sizeIncrement +20;
UIButton *likeButton = [[UIButton alloc] initWithFrame:CGRectMake(10, y, 80, 26)];
likeButton.tag = [indexPath section];
// add targets and actions
[likeButton addTarget:self action:#selector(likebuttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[likeButton setBackgroundImage:[UIImage imageNamed:#"likebutton.png"] forState:UIControlStateNormal];
[likeButton setImage:[UIImage imageNamed:#"likedButton.png"] forState:UIControlStateSelected];
// add to a view
[cell.contentView addSubview:likeButton];
.....similarly create 2 other buttons
You should move button creating into if (cell == nil) operator:
if (cell == nil)
{
cell = [[TipsCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
// like button
float y = 359.0f + sizeIncrement +20;
UIButton *likeButton = [[UIButton alloc] initWithFrame:CGRectMake(10, y, 80, 26)];
likeButton.tag = LIKE_BUTTON_TAG_UNIQUE_IN_CONTENT_VIEW; // <<<<<<<<<<----------------
// add targets and actions
[likeButton addTarget:self action:#selector(likebuttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[likeButton setBackgroundImage:[UIImage imageNamed:#"likebutton.png"] forState:UIControlStateNormal];
[likeButton setImage:[UIImage imageNamed:#"likedButton.png"] forState:UIControlStateSelected];
// add to a view
[cell.contentView addSubview:likeButton];
// other buttons
}
// and here you can adjust button positions
UIButton *likeButton = [self.contentView viewWithIndex:LIKE_BUTTON_TAG_UNIQUE_IN_CONTENT_VIEW];

IOS UITableViewCell Spacer (or margin)

I'm trying to get a space between my table custom view cells on my table view controller. For example, for each cell created it is stacked up one by one and there is no margin. For example on the facebook iPhone app has a spacer after each cell which i would like to create, any ideas guys?
Edit: From comment below
The code looks like this
MSRSalesCompanyCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
cell.backgroundView = [[CustomCellBackground alloc] init];
NSDictionary *company = [companies objectAtIndex:[indexPath row]];
cell.companyName.text = [company objectForKey:#"Name"];
cell.backgroundColor = [UIColor clearColor];
cell.backgroundView.bounds = CGRectMake(0, 0, 320, 55);
[self loadImageAsync:cell withImageUrl:imageURL];
Check my answer for the similar question, it suggests you to create the invisible cells between the cells you want to divide with some space.
Option 2 is to create only the visible cells, make their height above the visible area and prepare special content and selected view's, note the selected view creation is not so easy and you'll need to do it as you probably don't want the spacing area to get selected, so i'm using the first option when there's a need to get some cell's spacing.
The main (and probably the only) disadvantage of the first option is that you have to treat the cell's indexes in a special way to distinguish the spacing-cells and the visible-cells.
If your tableView's rowHeight is 50 points, make your background image view height say, 40 points, and centre it in the cell. Then make your cell background colour [UIcolor clearColor]
Okayy i did some thing like this.....
First the BGColor Of view which will hold the Tableview is set to White (it was a personal choice w.r.t to my design) and then the cellForRowAtIndexPath method looks some thing like this.
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell;
cell = nil;
//Create Cell
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
//Retrive Values From DB
DBTable_Description *dbObject = [[DBTable_Description alloc]init];
dbObject = [contentList objectAtIndex:[indexPath row]];
//Cell View
UIView *cellView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 65)];
//ImageView
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(4.0, 8.0, 300.0, 58.0)];
imageView.userInteractionEnabled = NO;
imageView.image = imgForCellBkg;
//Label
UILabel *lblFor = [[UILabel alloc]initWithFrame:CGRectMake(70, 25, 200, 21)];
lblFor.text =dbObject.field1;
lblFor.backgroundColor = [UIColor clearColor];
lblFor.font = [UIFont fontWithName:#"Helvetica Neue" size:16];
lblFor.textColor = [UIColor grayColor];
lblFor.shadowColor = [UIColor grayColor];
//Adding Views to Cell View
[cellView addSubview:imageView];
[cellView addSubview:lblFor];
[cell.contentView addSubview:cellView];
cell.selectionStyle = NO;
return cell;
}
Okay First and formost neglect the Database code.
Now what i did is i created a View on each Cell name cellView (set the height as per ur requirement) next i had an Image View and set the appropriate image (again it may not be the thing u want to do) but pay attention to the CGRectMake i se the values according to the amount of gap i want b.w my cells.. the rest is usual.
here is the image of the view i had in my App
Let me Know if it worked
Cheers
W

How to create these kind of badges?

I sketched this prototype: http://dl.dropbox.com/u/3630641/3-%20Todolist%20Main.jpg
The number at the left is the item priority and list is re-orderable so the values will change. How to create these left-located badges? I prefer a code approach if possible, not PNG images.
Update:
Would you please have a look:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
CGRect priorityLabelRect = CGRectMake(10, 5, 20, 30);
UILabel *priorityLabel = [[UILabel alloc] initWithFrame:priorityLabelRect];
priorityLabel.layer.cornerRadius = 5.0f;
priorityLabel.layer.backgroundColor = [[UIColor grayColor] CGColor];
priorityLabel.tag = kPriorityValueTag;
[cell.contentView addSubview:priorityLabel];
[priorityLabel release];
CGRect meatLabelRect = CGRectMake(80, 5, 300, 30);
UILabel *meatLabel = [[UILabel alloc] initWithFrame:meatLabelRect];
meatLabel.tag = kMeatValueTag;
[cell.contentView addSubview:meatLabel];
[meatLabel release];
cell.showsReorderControl = YES;
}
// Configure the cell.
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
But No grey area is shown around the priority.
If I add a new one, a grey area appears for less than a second around the priority but it disappears immediately. Not to mention that the priority value isn't located at the center for the grey area.
Any idea what I'm doing wrong?
Add a UILabel to the cell and format it to your liking. For the rounded corners, set label.layer.cornerRadius.
Create a UIView for the badge.
look here it will explaine how to draw what ever you want the badge to look like. and draw the number :
http://www.techotopia.com/index.php/An_iPhone_Graphics_Drawing_Tutorial_using_Quartz_2D
create a variable that will receive the cell number and draw it in the cell.
It should look something like that -
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2.0);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGRect rectangle = CGRectMake(10,10,40,40);
CGContextAddRect(context, rectangle);
CGContextStrokePath(context);
CGContextSetFillColorWithColor(context, [UIColor grayColor].CGColor);
CGContextFillRect(context, rectangle);
CGPoint point =CGPointMake(20,20);
[self.cellNumber drawAtPoint:point withFont:font];
}
4 add the BadgeView to your cell and in the CellForRawAtIndexPath pass the number to the badge view.
good luck
You could make a combination control. It's a UIImageView with a UIImage and a UILabel as subviews.
A regular object that has a UIImageView.image with the gray rectangle and then a UILabel for the white number. Then just change the value of the label's text.
If you start with a UIImageView and subclass that, you will be able to just stick it directly into the regular UITableViewCells since they already have a space for a UIImageView.