This is the tableView, as you can see, which cell, have two part, the left one, is a leftUIView, and right one, is the rightUIView. The red and green color can display, but the rightLabel I create can't show successfully. What's wrong with my code? Thank you.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *SectionsTableIdentifier = #"SectionsTableIdentifier";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:SectionsTableIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:SectionsTableIdentifier] autorelease];
}
cell.textLabel.textColor = [UIColor whiteColor];
UIView *rightUIView = [[UIView alloc] initWithFrame:CGRectMake(160, 0, 160, cell.frame.size.height)];
[rightUIView setBackgroundColor:[UIColor greenColor]];
UIView *leftUIView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 160, cell.frame.size.height)];
[leftUIView setBackgroundColor:[UIColor redColor]];
UILabel *rightLabel = [[UILabel alloc] init];
[rightLabel setText:#"dummy"];
[rightUIView addSubview:rightLabel];
[cell addSubView:leftUIView];
[cell addSubView:rightUIView];
}
The first problem with your code is that you create cell's subviews each time cell is requested - if you scroll your table to and fro your cell will have a bunch of those subviews - certainly that's not good. You must create cell subviews only once - when you create the cell and after that only set appropriate values to them:
const int kRightViewTag = 100;
const int kRightLabelTag = 200;
...
if (cell == nil) {
// Create everything here
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:SectionsTableIdentifier] autorelease];
cell.textLabel.textColor = [UIColor whiteColor];
UIView *rightUIView = [[UIView alloc] initWithFrame:CGRectMake(160, 0, 160, cell.frame.size.height)];
rightUIView.tag = kRightViewTag;
[rightUIView setBackgroundColor:[UIColor greenColor]];
[rightUIView release]; // Do not forget to release object you allocate!!
UIView *leftUIView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 160, cell.frame.size.height)];
[leftUIView setBackgroundColor:[UIColor redColor]];
[leftUIView release]; // Do not forget to release object you allocate!!
UILabel *rightLabel = [[UILabel alloc] initWithFrame:leftUIView.bounds];
rightLabel.tag = kRightLabelTag;
[rightUIView addSubview:rightLabel];
[rightLabel release];
}
// Setup values here
UILabel* rightLabel = (UILabel*)[[cell viewWithTag:kRightViewTag] viewWithTag:kRightLabelTag];
rightLabel.text = #"dummy";
The second thing is you do not set label's frame anywhere (I've fixed that above) so it is likely has zero-sized frame and that's why you cannot see it.
The 3rd problem is that you allocate views but don't release them - so they just leak. Do not forget that if you create some objects using alloc, new or copy then you're responsible for releasing them.
Related
I have a UITableView with each cell being broken into several sections with a different label in each section. The table gets populated by an NSArray of NSDictionaries which contain all the data that populates the cell's labels. This part of the UITableView works great.
The problem arises when I change some of the values in one of the NSDictionaries and then reload the table with the updated NSArray. Normally, when I call [myTableView reloadData]; nothing is updated even though (through debugging) I can see the updated data being processed. But if I change the standard: if (cell == nil) { to if (1) {, in the cellForRowAtIndexPath method, then it works beautifully. I understand why if(1) { works but I do not understand why I cannot reuse the cells and just change the label text.
Why does if (cell == nil) not work? Is it a huge resource drain to re-initialize each cell?
CODE:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"myCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: cellIdentifier];
if (/*cell == nil*/1) {
// Initialize Custom Cell
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
//// Background View
cellBackgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 622, 43)];
[cellBackgroundView setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"Images/Library/phase_table_cell.png"]]];
//// Name Label
nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 250, 18)];
[nameLabel setBackgroundColor:[UIColor clearColor]];
[cellBackgroundView addSubview:nameLabel];
//// Percent Complete Label
percentCompleteLabel = [[UILabel alloc] initWithFrame:CGRectMake(300, 10, 30, 18)];
[percentCompleteLabel setBackgroundColor:[UIColor clearColor]];
[percentCompleteLabel setTextAlignment:UITextAlignmentCenter];
[cellBackgroundView addSubview:percentCompleteLabel];
//// Overall Status Label
overallStatusLabel = [[UILabel alloc] initWithFrame:CGRectMake(352, 7, 63, 30)];
[overallStatusLabel setBackgroundColor:[UIColor clearColor]];
[overallStatusLabel setFont:[UIFont boldSystemFontOfSize:12.0]];
[overallStatusLabel setLineBreakMode:UILineBreakModeWordWrap];
[overallStatusLabel setNumberOfLines:2];
[overallStatusLabel setTextAlignment:UITextAlignmentCenter];
[cellBackgroundView addSubview:overallStatusLabel];
//// Finish Date Label
finishDateLabel = [[UILabel alloc] initWithFrame:CGRectMake(425, 10, 55, 18)];
[finishDateLabel setBackgroundColor:[UIColor clearColor]];
[finishDateLabel setTextAlignment:UITextAlignmentCenter];
[finishDateLabel setFont:[UIFont systemFontOfSize:12.0]];
[cellBackgroundView addSubview:finishDateLabel];
//// Overall Weight Label
overallWeightLabel = [[UILabel alloc] initWithFrame:CGRectMake(505, 10, 30, 18)];
[overallWeightLabel setBackgroundColor:[UIColor clearColor]];
[overallWeightLabel setTextAlignment:UITextAlignmentCenter];
[cellBackgroundView addSubview:overallWeightLabel];
//// Green Risk View
greenRiskView = [[UIView alloc] initWithFrame:CGRectMake(557, 4, 61, 10)];
[greenRiskView setBackgroundColor:[UIColor greenColor]];
[greenRiskView setHidden:YES];
[cellBackgroundView addSubview:greenRiskView];
//// Yellow Risk View
yellowRiskView = [[UIView alloc] initWithFrame:CGRectMake(557, 17, 61, 10)];
[yellowRiskView setBackgroundColor:[UIColor yellowColor]];
[yellowRiskView setHidden:YES];
[cellBackgroundView addSubview:yellowRiskView];
//// Red Risk View
redRiskView = [[UIView alloc] initWithFrame:CGRectMake(557, 30, 61, 10)];
[redRiskView setBackgroundColor:[UIColor redColor]];
[redRiskView setHidden:YES];
[cellBackgroundView addSubview:redRiskView];
[cell.contentView addSubview:cellBackgroundView];
}
// Get Current Dictionary
NSDictionary *dictForIndexPath = [self.phaseArray objectAtIndex:[indexPath row]];
// Set Elements
[nameLabel setText:[dictForIndexPath objectForKey:#"name"]];
[percentCompleteLabel setText:[dictForIndexPath objectForKey:#"percentComplete"]];
[overallStatusLabel setText:[dictForIndexPath objectForKey:#"overallStatus"]];
[overallWeightLabel setText:[[NSNumber numberWithInt:[[dictForIndexPath objectForKey:#"overallWeight"] intValue]] stringValue]];
//// Create Finish Date String
NSString *finishDateString = [NSString stringWithFormat:#"%#/%#/%#", [dictForIndexPath objectForKey:#"finishDay"], [dictForIndexPath objectForKey:#"finishMonth"], [dictForIndexPath objectForKey:#"finishYear"]];
[finishDateLabel setText:finishDateString];
//// Pick Risk View
NSString *riskColor = [dictForIndexPath objectForKey:#"riskColor"];
if ([riskColor isEqualToString:#"Green"]) {
[greenRiskView setHidden:NO];
[yellowRiskView setHidden:YES];
[redRiskView setHidden:YES];
} else if ([riskColor isEqualToString:#"Yellow"]) {
[greenRiskView setHidden:YES];
[yellowRiskView setHidden:NO];
[redRiskView setHidden:YES];
} else {
[greenRiskView setHidden:YES];
[yellowRiskView setHidden:YES];
[redRiskView setHidden:NO];
}
return cell;
}
Probably you are assigning the value within the if(cell==nil) block? Only the initialization should happen within that block. Move the rest out of it. (Post your complete cellForRow code, if you need more help)
//edit: now, after you posted your code i see your problem:
you are storing all your labels and views in member variables.. but of course that only happens, when the if(cell != nil) block is executed. After that, you always access the same single cell (that was assigned last). So you are updating at least one cell ;)
To fix your problem, work e.g. with tags to get your corresponding views back from the cell. I will show it for your backgroundView, but you have to do it for all of your views (INSTEAD of the member variables. Remove them.)
static NSString *cellIdentifier = #"myCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: cellIdentifier];
if (cell == nil) {
//// Background View
UIView* cellBackgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 622, 43)];
cellBackgroundView.tag = 1;
[cellBackgroundView setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"Images/Library/phase_table_cell.png"]]];
}
// get the backgroundview from the current cell
UIView* backgroundView = [cell.contentView viewWithTag: 1];
and so on..
When I call [table reloaddata];
The cells get redrawn with new data, but my UILabels get messed up because they are drawn over the old UILabels, so its a mess.
static NSString* PlaceholderCellIdentifier = #"PlaceholderCell";
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:PlaceholderCellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:PlaceholderCellIdentifier] autorelease];
cell.detailTextLabel.textAlignment = UITextAlignmentCenter;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.contentView.backgroundColor = [UIColor clearColor];
}
Is my Init of the cell.
I add a UILabel like so
UILabel *theDateLabel = [[UILabel alloc] initWithFrame:CGRectMake(140, 35,140, 20)];
[theDateLabel setBackgroundColor:[UIColor clearColor]];
[theDateLabel setTextColor:[UIColor lightGrayColor]];
[theDateLabel setText:[dateFormatter stringFromDate:theDate]];
[theDateLabel setFont:[UIFont fontWithName:#"TrebuchetMS-Bold" size:15]];
[cell addSubview:theDateLabel];
[theDateLabel release];
There are a few more labels in the cell, same thing.
What I would like to happen is that the old labels disappear from the cell so that they are no longer visible.
You should not add theDateLabel as a subview of cell. You should add it as a subview of cell.contentView.
As yuji suggests, one way to implement this is to create a subclass of UITableViewCell with a property for each custom subview. That way you can easily get to the date label of a reused cell to set its text for the new row.
Another common approach is to use the tag property that every UIView has. For example:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString* PlaceholderCellIdentifier = #"PlaceholderCell";
static const int DateLabelTag = 1;
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:PlaceholderCellIdentifier];
if (!cell) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:PlaceholderCellIdentifier] autorelease];
UILabel *theDateLabel = [[UILabel alloc] initWithFrame:CGRectMake(140, 35,140, 20)];
theDateLabel.tag = DateLabelTag;
theDateLabel.backgroundColor = [UIColor clearColor];
theDateLabel.textColor = [UIColor lightGrayColor];
theDateLabel.font = [UIFont fontWithName:#"TrebuchetMS-Bold" size:15];
[cell.contentView addSubview:theDateLabel];
[theDateLabel release];
}
NSDate *theDate = [self dateForRowAtIndexPath:indexPath];
UILabel *theDateLabel = [cell.contentView viewWithTag:DateLabelTag];
theDateLabel.text = [dateFormatter stringFromDate:theDate];
return cell;
}
While Richard's solution will work, if your cells have any other subviews they'll get removed as well. Also, allocating and initializing your subviews every time you draw a cell isn't necessarily optimal.
The standard solution here is to create a subclass of UITableViewCell with a property #dateLabel (and so on for the other labels). Then, when you're initializing a cell, if it doesn't have a #dateLabel yet you can give it a new one, otherwise you only have to set its text.
I have a table view cell with multiple images in them. When touching the images they shold display an overlay on top of the image which tells the user that this image was selected.
Is there a way to change the look of just one UITableViewCell without having to do a [tableView reloadData] which would allow me to style the cell differently in the table view datasource delegate method.
The way I would do it is to subclass UITableViewCell and then on tableView:didSelectRowAtIndexPath: get a reference to the cell and do whatever you want to it (or just target the image touch event if this is not a selection).
There might be another way of doing this without having to subclass, but I find myself subclassing UITableViewCell all the time and it's pretty straightforward to do.
If you wish to avoid subclassing, this can be achieved with gesture recognisers. Your question suggests a Tap and Hold user interaction on each image, which I have implemented in the code below. One point to remember, if the user is tapping and holding, they may not see the text you wish them to see.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"ImageCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
UILongPressGestureRecognizer *recognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(imageTapped:)];
UILongPressGestureRecognizer *recognizer2 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(imageTapped:)];
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Ben.png"]];
imageView.frame = CGRectMake(cell.contentView.bounds.origin.x,cell.contentView.bounds.origin.y , 100, 40);
imageView.userInteractionEnabled = YES;
[imageView addGestureRecognizer:recognizer];
[cell.contentView addSubview:imageView];
UIImageView *imageView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Steve.png"]];
imageView2.frame = CGRectMake(cell.contentView.bounds.origin.x + imageView.frame.size.width + 10,cell.contentView.bounds.origin.y , 100, 40);
imageView2.userInteractionEnabled = YES;
[imageView2 addGestureRecognizer:recognizer2];
[cell.contentView addSubview:imageView2];
[imageView release];
[imageView2 release];
[recognizer release];
[recognizer2 release];
return cell;}
- (void)imageTapped:(id)sender {
NSLog(#"%#", sender);
UILongPressGestureRecognizer *recognizer = (UILongPressGestureRecognizer *)sender;
if (recognizer.state == UIGestureRecognizerStateBegan) {
UILabel *label = [[UILabel alloc] initWithFrame:recognizer.view.bounds];
label.text = #"Pressed";
label.backgroundColor = [UIColor clearColor];
label.tag = 99999;
label.textColor = [UIColor whiteColor];
[recognizer.view addSubview:label];
[label release];
}
else {
[[recognizer.view viewWithTag:99999] removeFromSuperview];
}
}
Hope this helps.
I'm using the following code to create a custom UITableViewCell with a label in it.
The problem I have is that the cell doesn't get cleared when it's reused and I'm unsure of how to do so as I don't think I have access to the cell contents when it's dequeued.
Can anyone tell me how I'd do this please?
if (indexPath.section == 2) {
// Resetting the elements in the cell prior to use.
UIView *blankCell;
UILabel *methodText;
static NSString *methodCellIdentifier = #"Method Cell";
methodCell = [tableView dequeueReusableCellWithIdentifier: methodCellIdentifier];
if (methodCell == nil)
{
methodCell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier: methodCellIdentifier] autorelease];
}
blankCell = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
methodText = [[UILabel alloc] initWithFrame:CGRectMake(15, 7, 305, 44)];
methodText.text = [[self.recipeDict objectForKey:#"Method"] objectAtIndex: indexPath.row];
NSLog(#"methodText.text = %#", methodText.text);
[methodText setLineBreakMode:UILineBreakModeWordWrap];
[methodText setNumberOfLines:0];
[methodText sizeToFit];
[blankCell addSubview:methodText];
[methodCell.contentView addSubview:blankCell];
[methodText release];
methodCell.contentMode = UIViewContentModeRedraw;
return methodCell;
}
Move the code that creates your subviews inside the block that checks if methodCell is nil. Only update their content outside of that block. To access them outside the block, set tag values on them when creating them and use viewWithTag to find and update their content:
if (methodCell == nil)
{
methodCell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier: methodCellIdentifier] autorelease];
blankCell = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
methodText = [[UILabel alloc] initWithFrame:CGRectMake(15, 7, 305, 44)];
[methodText setLineBreakMode:UILineBreakModeWordWrap];
[methodText setNumberOfLines:0];
[blankCell addSubview:methodText];
[methodCell.contentView addSubview:blankCell];
methodText.tag = 1;
[methodText release];
[blankCell release];
methodCell.contentMode = UIViewContentModeRedraw;
}
methodText = (UITextField*)[methodCell viewWithTag:1];
methodText.text = [[self.recipeDict objectForKey:#"Method"] objectAtIndex: indexPath.row];
NSLog(#"methodText.text = %#", methodText.text);
[methodText sizeToFit];
this is the cell i want to make the left side is cell.text and the right part is label.
Now the table style is
UITableViewStyleGrouped When i try to make the label i write these codes.
cell.textLabel.text = #"All";
UIView* view = cell.contentView;
UILabel* label1 = [[UILabel alloc] initWithFrame:cell.frame];
label1.textColor = [UIColor blackColor];
label1.textAlignment = UITextAlignmentCenter;
label1.text = #"%15";
[view addSubview:label1];
[label1 release];
But this doesnt work because cell of the label cover one and another.Can any one help me to make this kind of look with code of course.
The problem in your code seems to be the label1's frame. Change the its frame like the following.
CGRect lFrame = CGRectMake(cell.frame.width - 100, 0, 100, cell.frame.height);
UILabel* label1 = [[UILabel alloc] initWithFrame:lFrame];
Using exisiting style: The style you are using is already predefined. No need to add your custom label to the cell. You can achieve this style by specifying table cell's style to UITableViewCellStyleValue1.
[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 ....
You can change the font properties like style, color and size of the cell's textLabel and detailedTextLabel to fit your needs.
Just make your UITableviewCell type to UITableViewCellStyleValue1 and do like below.
cell.textLabel.text = #"All";
cell.detailTextLabel.text = #"%15";
You don't need to add view in the cell at all. Also, you can change textLable and DetailTextLabel properties whatever you want as you change with UILabel. They are UILabel itself. So you can do everything whatever you can do with UILabel.
Hope this help.
The pre-defined styles are great if they cover your needs.
Otherwise an approach like this one will work for cases where you want more power over the layout, or more views in the cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [self makeCell: CellIdentifier];
}
MyData *data = [self.data objectAtIndex:indexPath.row];
UILabel *lbl1 = (UILabel *)[cell viewWithTag:1];
UILabel *lbl2 = (UILabel *)[cell viewWithTag:2];
lbl1.text = data.text;
lbl2.text = data.auxText;
return cell;
}
- (UITableViewCell *)makeLensListCell: (NSString *)identifier
{
CGRect lbl1Frame = CGRectMake(10, 0, 140, 25);
CGRect lbl2Frame = CGRectMake(10, 150, 140, 25);
UILabel *lbl;
UITableViewCell *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier] autorelease];
// Label with tag 1.
lbl = [[UILabel alloc] initWithFrame:lbl1Frame];
lbl.tag = 1;
[cell.contentView addSubview:lbl];
[lbl release];
// Label with tag 2.
lbl = [[UILabel alloc] initWithFrame:lbl2Frame];
lbl.tag = 2;
lbl.textColor = [UIColor lightGrayColor];
[cell.contentView addSubview:lbl];
[lbl release];
// Add as many labels and other views as you like
return cell;
}