I'm experiencing a problem with regards to adding a button to a tableview cell. Basically, I'm adding a button to a specific cell, but that button also appears on random other cells (not currently in the view).
When I tap a cell, I set a variable (tappedCell=indexPath), and reload that cell. In my cellForRowAtIndexPath I check if the indexPath is equal to the tappedCell, and if it is I add a button to it. That works, but this button also appears on other cells further down in the tableView. These cells are not in the view when I tap it, but further down, so I have to scroll to see them. If I keep scrolling up and down (quickly), more and more cells with buttons appears. This seems to be completely random.
I've tried adding a NSLog() inside the if-statement where I add the button, but this is not called more than once (when I tap the original cell). So it's actually not adding more buttons, it has to be the same one appearing in multiple cells.
I have no idea why this is happening. I've tried adding a button to every cell, and then just set hidden = YES as standard, and hidden = NO when the [self.tappedCell isEqual:indexPath], but this does not change anything...
Here you can see the code I use to add the button to my cell:
UIButton *newBtn=[UIButton buttonWithType:UIButtonTypeCustom];
[newBtn setImage:[UIImage imageNamed:#"open.png"] forState:UIControlStateNormal];
[newBtn setFrame:CGRectMake(220, 0, 100, 86)];
[newBtn addTarget:self action:#selector(openImage:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:newBtn];
I don't know what I'm doing wrong, or if I am doing something wrong at all. It's worth mentioning that I'm using a standard Apple/iOS style=subtitle cell, and not a custom one. This is to keep it consistent throughout my app (The cells are made in the storyboard, and I'm reusing a prototype cell).
Could anyone give me any insight on why it's behaving like it does, and how I can fix it?
Thanks in advance!
EDIT 1:
Here's my full code for cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Set up the item
XYZItem *item = [self.itemsInView objectAtIndex:indexPath.row];
NSString *CellIdentifier = #"itemPrototypeCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.textLabel.text = item.itemName;
if ([self.selectedItem isEqual:indexPath])
{
cell.textLabel.numberOfLines = 2;
UIButton *newBtn=[UIButton buttonWithType:UIButtonTypeCustom];
[newBtn setImage:[UIImage imageNamed:#"open.png"] forState:UIControlStateNormal];
[newBtn setFrame:CGRectMake(220, 0, 100, 86)];
[newBtn addTarget:self action:#selector(openImage:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:newBtn];
}
NSString *detailTextLabelText = [NSString stringWithFormat:#"%lu subitems", (unsigned long)[item.subItems count]];
cell.detailTextLabel.text = detailTextLabelText;
return cell;
}
The button is showing up on cells that have reused a button that previously added it.
There are a few options here.
Make the button part of every cell. Set it's .hidden property every time you create/reuse one to the appropriate value.
Remove the button in the cell's prepareForReuse method.
Assuming self.tappedCell is a UITableViewCell subclass and indexPath is an index path, [self.tappedCell isEqual:indexPath] will always return NO
I faced same issue and i also had multiple headers:
The solution is:
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
headerLabel = [[UILabel alloc] initWithFrame:
CGRectMake(15, 5, self.tableView.frame.size.width, 15.0)];
headerLabel.backgroundColor = [UIColor clearColor];
headerLabel.textAlignment = NSTextAlignmentLeft;
[headerLabel setFont:[UIFont fontWithName:#"Helvetica Neue Medium" size:13.0]];
headerLabel.tag = 1002;
[cell.contentView addSubview:headerLabel];
button = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, HEIGHTROW)];
[button addTarget:self action:#selector(eventAddAlert) forControlEvents:UIControlEventTouchUpInside];
[button setBackgroundImage:[UIImage imageNamed:#"add_manually.png"] forState:UIControlStateNormal];
button.hidden = YES;
button.tag = 1001;
[cell.contentView addSubview:button];
}else{
// use viewWithTag to find lblNombre in the re-usable cell.contentView
headerLabel = (UILabel *)[cell.contentView viewWithTag:1002];
button = (UIButton *)[cell.contentView viewWithTag:1001];
}
if(indexPath.section == 0 && indexPath.row == 0){
button.hidden = NO;
}else{
button.hidden = YES;
.........
}
.........
Related
I have a UITableView that exceeds the screen region. I display some data from an NSMutableArray which is filled with NSDictionary objects. The objects all have a group property which is true or false, if group is true a UIButton should be displayed in the cell (to expand the group).
I created a UIButton in my cell prototype in storyboard. I gave it alpha 0. If I load the list the button is not in any cell (duh). This is in my cellForRowAtIndexPath:
if ([[contacts[indexPath.section] valueForKey:#"group"] isEqualToString:#"true"]) {
UIButton *button = (UIButton*)[cell viewWithTag:999];
[button setAlpha:1.0f];
}
If the group property is true make the button visible. In my current test case the first three objects have group set to true, the rest have group set to false. Now my problem is, the first three cells have a visible button (as intended), but some cells further along the table have one as well. I put a breakpoint in the above if statement, and it really goes in there only three times.
It happens in a pattern, the first three cells have a button, than five cells don't and than another three have a visible button. Than another five have no button and the last cell does (I think if I add two cells they will also have a button)
The second group of cells that have a button starts just outside the screen, so that may have something to do with it.
One more relevant thing, There is also some text displayed that I take from the same array as the group flag, this text is displayed correct. So it's not that the cells are duplicated somehow, it's just the if statement part that screws up.
I double checked the amount of times the program goes into the if statement and it's really only three times (the first three entries). If I comment the setAlpha call on the button no buttons appear anywhere, so there is also no other place where the button can become visible.
I'm testing on an iPad 4 with iOS 7.1.
If I decrease the height of my rows so all of them fit in the screen, the problem is gone, so It's definitely related to rows falling outside the screen region while loading the data.
edit
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
UIColor *color = [UIColor colorWithRed:(1.0f/255.0f)*220 green:(1.0f/255.0f)*220 blue:(1.0f/255.0f)*225 alpha:1.0f];
if (indexPath.row == 0) {
cell = [tableView dequeueReusableCellWithIdentifier:#"single" forIndexPath:indexPath];
UILabel *screenname = (UILabel*)[cell viewWithTag:10];
UILabel *username = (UILabel*)[cell viewWithTag:11];
UIImageView *avatar = (UIImageView*)[cell viewWithTag:20];
if ([[contacts[indexPath.section] valueForKey:#"group"] isEqualToString:#"true"]) {
NSDictionary *c = contacts[indexPath.section];
UIButton *button = (UIButton*)[cell viewWithTag:999];
[button setAlpha:1.0f];
button.tag = indexPath.section;
[button addTarget:self action:#selector(onGroupButtonClick:) forControlEvents:UIControlEventTouchUpInside];
int numUsersInGroup = [[c valueForKey:#"users"] count];
NSString *numUsersInGroupString = [NSString stringWithFormat:#"%d",numUsersInGroup];
[button setTitle:numUsersInGroupString forState:0];
[button setNeedsDisplay];
screenname.text = #"Groep";
username.text = [NSString stringWithFormat:#"%#",[c valueForKey:#"groupName"]];
} else {
User *c = (User*)contacts[indexPath.section];
UIButton *button = (UIButton*)[cell viewWithTag:999];
[button setAlpha:0.0f];
screenname.text = [NSString stringWithFormat:#"%#",[c valueForKey:#"screenName"]];
username.text = [NSString stringWithFormat:#"%#",[c valueForKey:#"username"]];
[avatar setImage:[UIImage imageNamed:#"avatar.png"]];
}
[GeneralFunctions setFontFamily:#"KozGoPro-ExtraLight" forView:cell andSubViews:YES];
[GeneralFunctions setFontFamily:#"KozGoPro-Medium" forView:screenname andSubViews:YES];
[cell.layer setShadowColor:[color CGColor]];
[cell.layer setShadowOffset:CGSizeMake(0,4)];
[cell.layer setShadowRadius:3.0f];
[cell.layer setShadowOpacity:1.0f];
cell.clipsToBounds = NO;
cell.layer.masksToBounds = NO;
}
if (indexPath.row > 0) {
cell = [tableView dequeueReusableCellWithIdentifier:#"group" forIndexPath:indexPath];
UILabel *screenname = (UILabel*)[cell viewWithTag:10];
UILabel *username = (UILabel*)[cell viewWithTag:11];
User *c = (User*)[contacts[indexPath.section] valueForKey:#"users"][indexPath.row-1];
screenname.text = [NSString stringWithFormat:#"%#",[c valueForKey:#"screenName"]];
username.text = [NSString stringWithFormat:#"%#",[c valueForKey:#"username"]];
[GeneralFunctions setFontFamily:#"KozGoPro-ExtraLight" forView:cell andSubViews:YES];
[GeneralFunctions setFontFamily:#"KozGoPro-Medium" forView:screenname andSubViews:YES];
}
return cell;
}
When your cell is reused you are not setting the button back to the 0.0 alpha. You need to reset your cell or change your cell's property whenever you reuse a cell otherwise it'll show the old content
if ([[contacts[indexPath.section] valueForKey:#"group"] isEqualToString:#"true"]) {
NSDictionary *c = contacts[indexPath.section];
UIButton *button = (UIButton*)[cell viewWithTag:999];
[button setAlpha:1.0f];
//this line changes your tag and hence in else condition for reused cell it'll come nil
button.tag = indexPath.section;
} else {
UIButton *button = (UIButton*)[cell viewWithTag:999];
[button setAlpha:0.0f];
}
Suggestion instead of accessing button by tag access it by using property by type casting the cell into your custom cell
I have a subview within UITableViewCell's that contains a UILabel and a UIButton. When I push the button it unselects the UITableViewCell for some reason.
Is there a way to pass the touch event through to its super view? Or is there a different problem all together?
Here is the code for CellForRowAtIndexPath. I plan on creating a custom subclass of UITableViewCell so I can handle cell reuse properly so don't feel the need to mention that.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CountModel *cellModel = self.viewModel.data[indexPath.row];
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"RootViewControllerMetricCell"];
cell.textLabel.text = cellModel.title;
// Custom background color for selection
UIView *bgColorView = [[UIView alloc] init];
bgColorView.backgroundColor = UIColorFromRGB(0xF3F3F3);
bgColorView.layer.masksToBounds = YES;
[cell setSelectedBackgroundView:bgColorView];
// Construct subview and append to bottom (only visible when selected)
UIView *cellSubView = [[UIView alloc] initWithFrame:CGRectMake(0, 125, cell.frame.size.width, 25)];
// UILabel for display of current count
UILabel *lblCount = [[UILabel alloc] initWithFrame:CGRectMake(15, 0, 25, 25)];
lblCount.text = [NSString stringWithFormat:#"%d", [cellModel.count intValue]];
// Create UIButton for incrementing
UIButton *incrementButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
incrementButton.frame = CGRectMake(45, 0, 25, 25);
[incrementButton setTitle:#"+" forState:UIControlStateNormal];
// Enable touch event for button view
[incrementButton addTarget:self action:#selector(countButtonTouchDown:) forControlEvents:UIControlEventTouchDown];
[cellSubView addSubview:lblCount];
[cellSubView addSubview:incrementButton];
[cell addSubview:cellSubView];
return cell;
}
How about you add the [self.tableView cellForRowAtIndePath:] setSelected:] inside the function responding to the button press to reinstate the same selected/ deselected state.
UPDATE:
Shift the code inside the didSelectCellAtIndexPath to another function, say performActionOnSelectionOfCellAtIndexPath and call performActionOnSelectionOfCellAtIndexPath from inside the didSelectCellAtIndexPath with the indexPath and also call performActionOnSelectionOfCellAtIndexPath from the function responding to the button press.
Yesterday I asked a question about a cell not showing correctly a button that depends on a string value. Please take a look at it if you want: Strange behaviour on table view with core data.
User #jrturton pointed out following as part of his answer:
A reused cell will have the subview added every time - so there could
be many urgent views on top of each other. The cell should only ever
add this once and keep it in a property
I think that this answer marks the correct direction I must follow to solve my issue, but I am not able to implement the answer into my code which is following:
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *managedObject = [fetchedResultsController objectAtIndexPath:indexPath];
NSString *isUrgent = [[managedObject valueForKey:#"urgent"]description];
[[cell textLabel] setText:[[managedObject valueForKey:#"thingName"] description]];
//urgent
if ([isUrgent isEqual:#"Urgent"]){
UIButton *urgentButton = [[UIButton alloc]initWithFrame:CGRectMake(71, 27, 18, 18)];
[urgentButton setImage:[UIImage imageNamed:#"urgent-3"]forState:UIControlStateNormal];
[cell addSubview:urgentButton];
NSLog(isUrgent);
}
//not urgent
if ([isUrgent isEqual:#"Not urgent"]){
UIButton *urgentButton = [[UIButton alloc]initWithFrame:CGRectMake(71, 27, 18, 18)];
[urgentButton setImage:[UIImage imageNamed:nil]forState:UIControlStateNormal];
[cell addSubview:urgentButton];
NSLog(isUrgent);
}
[[cell detailTextLabel] setText:#" "];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
cell.textLabel.textColor = [UIColor blueColor];
cell.textLabel.font = [UIFont fontWithName:#"Noteworthy" size:22.0f];
cell.detailTextLabel.font = [UIFont fontWithName:#"Noteworthy" size:15.0f];
}
The behaviour of the cell must be following:
1. If isUrgent = #"Urgent", the cell must show urgentButton (including imageNamed:#"urgent-3":
2. Else no button has to be shown.
The current behaviour is as follows:
1. If isUrgent = #"Urgent", the cell shows urgentButton (including imageNamed:#"urgent-3".
2. If isUrgent = #"Not urgent", value tested in NSLog, the cell shows urgentButton too.
This behavior only happens when the cell has changed its isUrgent value at least one time.
I need your help to implement the above mentioned answer. Thank you.
I agree with #wuii, but I think the answer can be clearer. The idea is that reused cells have their view hierarchy already built, so it's harmful to do it again each time a cell is reused (which is all of the time during scrolling). The advice can be encapsulated in a "lazy getter" that returns the cell's urgent button.
// above #implementation
#define kURGENT_BUTTON_TAG (256)
- (UIButton *)urgentButtonInCell:(UITableViewCell *)cell {
UIButton *urgentButton = (UIButton *)[cell viewWithTag:kURGENT_BUTTON_TAG];
if (!urgentButton) {
urgentButton = [[UIButton alloc]initWithFrame:CGRectMake(71, 27, 18, 18)];
urgentButton.tag = kURGENT_BUTTON_TAG;
[cell addSubview:urgentButton];
}
return urgentButton;
}
Now your configureCell can just ask for the button:
UIButton *urgentButton = [self urgentButtonInCell:cell];
UIImage *image = ([isUrgent isEqualToString:#"Urgent"])? [UIImage imageNamed:#"urgent-3"] : nil;
[urgentButton setImage:image forState:UIControlStateNormal];
You will need to keep track of your cell is reused or it's fresh one.
What here might be happening is you are adding UIButton to reused cell, but it already has button having "urgent-3" image set
Do this
Pass one more BOOL parameter to configure cell isFreshCell with value set as per new cell or not.
**
MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
isFreshCell = NO;
if(cell==nil)
{
isFreshCell = YES;
cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
[self configureCell:cell atIndexPath:indexPath isFreshCell:isFreshCell];
New method signature
-(void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath isFreshCell:(BOOL) isFreshCell
2 When you add button set some tag to it.
3 If isFreshCell is false dont add new button as it is already there and you can access this button using subviewWithTag method like this cell.contentView.subviewWithTag and then set image or set nil image or just hide button
I have added 2 button in a row of table view, for all the rows, and these button clicked as it first time appear in table view , when I scroll the table list the button tapping disable,
here is my code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ImageOnRightCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
cell.userInteractionEnabled = NO;
UIButton *finalPriceBtn=[UIButton buttonWithType:UIButtonTypeCustom];
UIButton *finalPriceBtn1=[UIButton buttonWithType:UIButtonTypeCustom];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
int i=indexPath.row;
finalPriceBtn.backgroundColor=[UIColor redColor];
finalPriceBtn.tag=MAINLABEL_TAG;
finalPriceBtn.frame = CGRectMake(200, 0.0, 100, 50);
[finalPriceBtn addTarget:self action:#selector(goBtnClk:) forControlEvents:UIControlEventTouchUpInside];
finalPriceBtn.titleLabel.font=[UIFont systemFontOfSize:12];
finalPriceBtn.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
[finalPriceBtn setImage:[UIImage imageNamed:#"man.jpg"] forState:UIControlStateNormal ];
[cell.contentView addSubview:finalPriceBtn];
finalPriceBtn1.backgroundColor=[UIColor redColor];
finalPriceBtn1.tag=SECONDLABEL_TAG;
finalPriceBtn1.frame = CGRectMake(50.0, 0.0, 80.0, 45.0);
[finalPriceBtn1 addTarget:self action:#selector(goBtnClk:) forControlEvents:UIControlEventTouchUpInside];
finalPriceBtn1.titleLabel.font=[UIFont systemFontOfSize:12];
finalPriceBtn1.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
[finalPriceBtn1 setImage:[UIImage imageNamed:#"bulk-female.jpg"] forState:UIControlStateNormal ];
[cell.contentView addSubview:finalPriceBtn1];
}
else
{
finalPriceBtn = (UIButton *)[cell.contentView viewWithTag:MAINLABEL_TAG];
finalPriceBtn1 = (UIButton *)[cell.contentView viewWithTag:SECONDLABEL_TAG];
}
return cell;
}
It is happening because, each time you are scrolling the tableview, your cells are reused and in that case cell is not nil and the code above the code before the cell==nil, makes the userInteractionEnabled to NO. That's why, your button is not clickable.
First time those buttons are clickable because they were not allocated, I mean the cell was not allocated and setting any attribute to non-allocated entity makes no effect. Hope you got the point.
I'm trying to add subviews to a view according to the cell of the tableview .. i've done this code already
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
VerseCell *cell = (VerseCell *)[tableView cellForRowAtIndexPath:indexPath];
rowSelected = indexPath.row;
CGRect cellRect = cell.frame;
UIView *cellView =[[UIView alloc]initWithFrame:CGRectMake(cellRect.origin.x+4, cellRect.origin.y+15, cellRect.size.width-20, 70)];
UIImageView* background =[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"FavoritePanelBackground"]];
background.frame =CGRectMake(cellRect.origin.x+4, cellRect.origin.y+15, cellRect.size.width-20, 70);
[cellView addSubview:background];
[cellView setBackgroundColor:[UIColor redColor] ];
UIButton * favoritsB = [[UIButton alloc]initWithFrame:CGRectMake(cellRect.origin.x+10, cellRect.origin.y+15, 60, 40)];
[favoritsB setBackgroundImage:[UIImage imageNamed:#"MofadalatNormalIcon.png"] forState:UIControlStateNormal];
[favoritsB setBackgroundImage:[UIImage imageNamed:#"MofadalatTappedIcon.png"] forState:UIControlStateHighlighted];
[favoritsB addTarget:self action:#selector(favorite) forControlEvents:UIControlEventTouchUpInside];
[cellView addSubview:favoritsB];
cellView=nil;
}
the problem is .. that this code work as expected in the first row only.. and when click on next cell it shows the main UIVIew (which i've mark with red background) in the expected position while the sub views of it is going really far down... so what i'm doing wrong here.. and is there another approach for what i'm doing if that will not work?
Where are you trying to add these view? Inside the table view cell? If so, you never add cellView as a subview of cell. IF that's what you're trying to do, add this line just above cellView = nil:
[cell addSubview:cellView];