I try to display a tableView with a button on each cell. The title of each button is a variable (an ID) in order to keep this value.
NSString *valuebutton = [NSString stringWithFormat:#"%#", [contactInfoDict objectForKey:#"idparse"]];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(cell.frame.origin.x, cell.frame.origin.y + 12, 300,35);
[button addTarget:self action:#selector(customActionPressed:) forControlEvents:UIControlEventTouchUpInside];
button.backgroundColor= [UIColor clearColor];
[button setTitle:valuebutton forState:UIControlStateNormal];
[cell.contentView addSubview:button];
return cell;
When I try to scroll the tableview on the device the value of the button change and I can see it be modified in live.
Did you see this issue before ?
Thanks for your help !
Alex.
Without more details it is difficult to debug. However, if you are using dynamic cells then any off screen cells are candidates for being recycled. If you use static cells, then any off screen cells are retained (not recycled). It is possible that off screen cells are being reused (assuming you are using dynamic cells) producing the behavior you described. If you are having issues with your cellForRowAtIndexPath method then that could be the issue too. Please share that code.
Also, looking at your initial code, you might want consider a dynamic prototype cell that includes the button in IB. Then all you need to do is reference the cell within cellForRowAtIndexPath, grab the button of the cell and set its title via the cell indexPath (i.e. cell 0 gets title 0, cell 1 gets title 1, etc.) -- would save you a bit of code.
Related
I have UICollectionView with custom flow layout, which purpose is availability to delete items. In order to retrieve index i use function:
-(void)aMethod:(UIButton*)sender{
[self.viewModel deleteAt:[sender tag]];
[self.myCollectionView reloadData];
}
Sometimes (in rare cases) i got crash. When i dig into it, i found that sometimes [sender tag] was incorrect, in fact, higher then array of items count. Why is that happen? I found that it send 8, when array only had 5 items.
Button is simple 40x40 width/height image, placed above UITableViewCell like this:
UIButton *button = [UIButton new];
[button setImage:[UIImage imageNamed:#"m_delete"] forState:UIControlStateNormal];
if (self.shouldEdit){
self.layout.longPressGestureRecognizer.minimumPressDuration = 0.3f;
NSLog(#"1 blk called");
[button addTarget:self
action:#selector(aMethod:)
forControlEvents:UIControlEventTouchUpInside];
[button setTag:indexPath.row];
[cell addSubview:button];
[button mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(cell.mas_left).with.offset(2);
make.top.equalTo(cell.mas_top).with.offset(2);
}];
}
I suspect that it's reusing old cell without updating the button's tag. Since you have conditions if (self.shouldEdit) which means the [button setTag:indexPath.row]; is not always called. You mentioned that the purpose of the buttons is to delete items so it might be that the tableView reused old / deleted cell (the one with tag 8) as one of the 5 cells and it doesn't call the setTag
You can take the setTag line outside the conditional so it will always update the button's tag every time the cell is created / reused.
Alternatively you can make sure the old button are removed from cell before reuse in UITableViewCell's prepareForReuse or inside cellForRowAtIndexPath right after dequeueReusableCellWithReuseIdentifier(...). Doing this you must always add new button because the old ones are removed from the cell
button tag is override every time when cellForRowAtIndexPath callled so dont set tag in that method
remove this line
[button setTag:indexPath.row];
use this one
-(void)aMethod:(UIButton*)sender
{
CGPoint point=[sender convertPoint:CGPointZero toView:collectiewname];
NSIndexPath *indexPath=[collectiewname indexPathForItemAtPoint:point];
NSLog(#"row :%ld",(long)indexPath.row)
}
I have a button within a tableView that doesn't behave as I would like.
I've placed my button initialisation within my cell==nil block in the tableView cellForRowAtIndexPath. When I move it out of this block the button is drawn multiple times with all the other buttons on top so I'm unable to move it (unless this is set up wrong).
My button is a simple green tick or red cross and is set up with a selector. Each row height within the tableView differs depending on the amount of text within it. My button however, when in the cell==nil part, doesn't take into consideration cell.frame.height. It reuses the height of the cells from the first time the tableView is initialised.
This is the code for the button:
button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button =[[UIButton alloc]init];
float framePos_x = cell.frame.size.width/1.30;
float framePos_y = cell.frame.size.height - 55;
NSLog(#"%f",cell.frame.size.height);
button.frame = CGRectMake(framePos_x, framePos_y, 35, 35);
buttonImage = [UIImage imageNamed:#"Notification_Accepted"];
buttonImage_Pressed = [UIImage imageNamed:#"Notification_Reject"];
[button setBackgroundImage:buttonImage forState:UIControlStateNormal];
[button setImage:buttonImage_Pressed forState:UIControlStateSelected];
button.tag = [UserID intValue];
[button addTarget:self action:#selector(ResponseFlagButton:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:button];
The output of my NSLog is:
2014-08-03 17:03:49.724 TableApp[5540:60b] 120.000000
2014-08-03 17:03:49.774 TableApp[5540:60b] 100.000000
2014-08-03 17:03:49.853 TableApp[5540:60b] 100.000000
2014-08-03 17:03:49.936 TableApp[5540:60b] 100.000000
2014-08-03 17:03:51.287 TableApp[5540:60b] 100.000000
When this log is within the cell=nil block that's all it outputs, even when scrolling up and down. Out of the cell=nil block and it works fine - same as the button (except for it's redraw issue).
How would I get the button to be drawn once each time without it being place on top of the old button. As well as taking into consideration the variable cell height?
I would do a combination of both Michal's and Lev's answers.
Subclass UITableViewCell and make the button a property.
Instantiate the button during the cell's own instantiation. This way you will only be adding the button once.
Override the prepareForReuse method of your UITableViewCell subclass and change only the appearance of the button as needed.
Override the layoutSubviews method of your UITableViewCell subclass and calculate and set the new height for the cell. (You are implementing your own custom intrinsic size here. Cool!)
Subclass UITableViewCell and calculate the button's frame in cell's layoutSubviews method.
I believe you need to subclass UITableViewCell and implement the prepareForReuse method. In prepareForReuse you would remove the button. Not a 100% certain that will solve the issue you are describing.
Subclassing UITableViewCell might also make it easier to calculate the button size.
I'm trying to create an friend request function. Where all the friend request would show up in a table and a player will get to click accept or decline. What I'm trying to do is create an accept button beside this UITableView that contains all the player's friend requests.
Here's my code.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *notificationCell = [tableView dequeuREusableCellWithIdentifier#"notificationCell" for IndexPath:indexPath];
NSArray *friendRequests = [self fetchAllFriendRequestsInArray];
NSManagedObject *friendRequestingRelationship = [friendRequests objectAtIndex:indexPath.row];
notificationCell.textLabel.text = [friendRequestingRelationship valueForKey:#"name"];
UIButton *acceptButton = [UiButton buttonWithType:UIButtonTypeSystem];
[acceptButton.frame = CGRectMake(notificationCell.frame.origin.x + 150, notificationcell.frame.origin.y -20, 80, 40);
[acceptButton setTitle:#"Accept" forState:UIControlStateNormal];
acceptButton.backgroundColor = [UIColor clearColor];
[acceptButton addTarget:self action:#selector(acceptButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[notificationCell.contentView addSubview:acceptButton];
return notificationCell;
}
Only the first notificationCell showed the friendrequester's name and Accept Button. Other notificationCells only showed other friendrequesters' names without the button. May I know what is wrong with my code such that I can allow the button to be shown on every single cell?
Thank you in advance!
The buttons are there, but they are clipped from the view. This line is the culprit:
acceptButton.frame = CGRectMake(notificationCell.frame.origin.x + 150, notificationcell.frame.origin.y -20, 80, 40);
You shouldn't add the origin of notificationCell to the button, because subview positions are relative to positions of their superviews.
This should give you the right look, but your code has other potential problems.
The line where you fetch all friend requests is probably too slow to be executed for each cell in the view. Make sure that the results are cached
Table view cells are recycled. When one of such recycled cells makes it to your code, it looks like your code adds a second button on top of the first one
Similarly, if a recycled cell with a button is returned for the cell that does not need a button, the old button would remain visible.
You may be better off using a prototype cell that already has a button on it. Instead of adding and removing that button, you could make the existing one visible or invisible, depending on the context.
This is a bad approach to solving this problem to begin with. You should go to the storyboard, and drop a button in a prototype cell. Then select that button, go to the attributes inspector, and set the "tag" to a number of your choice (for this example we will say 1). You can then get the button for each cell like so after grabbing the cell:
UIButton * acceptButton = (UIButton *)[cell viewWithTag: 1];
[acceptButton addTarget:self action:#selector(acceptButtonPressed) forControlEvents:UIControlEventTouchUpInside];
This is cleaner and will give you the results you want.
I'm facing a paramount problem that led me almost to toss my computer out of the window.
I'm trying to create a button only on some cells, I don't have any problem about triggering the action, but when I scroll down the table and I come back to the first cells the button is created on other cells. In other words, if cells 1 and 3 are supposed to have the button, when the tableview is created they are the only ones having the button. When I scroll down and up again also cell 2, 3 and 4 have the button (there is not a specific rule). The button is also working perfectly but it is not supposed to be there!
static NSString *CellIdentifier = #"Cell";
OpinionCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell= [[OpinionCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
...some influent code.......
if(([[aComment objectForKey:#"TypeMsg"] intValue]==310)&&([[parentMessage objectForKey:#"TypeMsg"] intValue]==310)){
UIButton *_openReplyButton = [[UIButton alloc] initWithFrame:CGRectMake(280, 5, 20, 20)];
[_openReplyButton setImage:[UIImage imageNamed:#"reply_button.png"] forState:UIControlStateNormal];
[_openReplyButton addTarget:self action:#selector(addRowsForShowReply:) forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:_openReplyButton];
NSLog(#"%#", [aComment objectForKey:#"Message"]);
}
Thank you very much for your help!
This is a classic problem for UITableView. It took me ages to figure out the dequeuing and reusing process that the table view does. Here's what should fix it.
Move the code to initialize the button into the part that checks whether cell == nil. This is because you're not supposed to add subviews to cells that have just been dequeued, because you don't know whether that cell has already had the subview added to it. Also, you should either set a tag for the button or make it a property of the OpinionCell. That way you can access it later.
Then, if you have determined that the button should be visible, set cell.replyButton.hidden = NO or [cell viewWithTag:kMyButtonTag].hidden = NO. Very importantly, you should set it to be hidden in an else clause. Otherwise, the button will show on seemingly random cells.
Hope this helps!
You can use following code to remove the subviews from UITableViewCell right after when the
cell is dequeued or initialised as it will remove all its subviews or you can follow what dado728 has mentioned above.
[[cell subviews] performSelectorOnMainThread:#selector(removeFromSuperview) withObject:nil waitUntilDone:NO];
I have setup a tableview that currently has one section, header, and two cells. For username and password, image shown below.
I would like to add a button (IBOutlet), and a label/link below this button within the Table View. What is the correct way to approach this programatically as I am not using interface builder at the moment.
Should the button be a new cell and section? Should the button be a header view and if so how do I control the height on only this header view?
you can try this inside your cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
where index path.row == 3
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
btn.frame = CGRectMake(cell.contentView.frame.size.width/2,10,100,20);
[btn setTitle:#"Login" forState:UIControlStateNormal];
[btn addTarget:self action:#selector(btnPressed:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:btn];
sorry for naming convention
I would suggest just adding another row to your table. If you add a second section with only one row the grouped tableview style will make it look like a simple button. If you need more complex styling you can of course create a custom UITableViewCell with one or more UIImageView(s).
Regarding the actually touch event I would then just stick to the tableView:didSelectRowAtIndexPath: delegate method.
If you have only one touch event for the whole UITableViewCell there isn't really a need to add a UIButton.
I would suggest to use simple text fields instead of a table view. However, if you want the table view approach, you should use another section, and make that button a table cell.