iOS - UIButton in selected cells of UITableView - ios

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];

Related

cellForRowAtIndexPath method custom button show 2 time

I used this code in cellForRowAtIndexPath method. When I click on button or scroll table this button is shown two times. Why is this button shown 2 times please help me?
UIButton *trashbtn=[[UIButton alloc]initWithFrame:CGRectMake(cell.frame.size.width-20, cell.frame.size.height-30, 20, 20)];
[trashbtn setImage:[UIImage imageNamed:#"editor_trash"] forState:UIControlStateNormal];
[trashbtn addTarget:self action:#selector(DeleteMyAssociate:) forControlEvents:UIControlEventTouchUpInside];
[trashbtn setTag:indexPath.row];
[cell addSubview:trashbtn];
You should firstly add a UITableViewCell within your table in IB. Then give an identifier e.g. "MyCellIdentifier" to that cell. Still on IB, add your outlets, to that cell e.g. the button, the textFields... You can initially set the button to be invisible. Then in the method cellForRowAtIndexPath, you do:
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCellIdentifier"];
if(cell){
myButton.hidden = NO; //
myTextField.text = #"BlaBlaBla";
}
}
Hey I can give you a suggestion that use auto layout and storyboard to add button rather than adding it programmatically. That is more better and cleaner approach.
The problem you are facing is that cells are reused in tableviews, So the first time you create a button and add it to the cell, it appears once. But if it is dequeued, it already has the button, so when you add another one you end up with multiple buttons.
There are two ways to correct this.
Firstly, remove the button in the cell's prepareForReuse method, which is called just after the cell is reused.
Secondly, avoid creating custom views in the cellForTableView... method. Use a custom cell that already has the button. Now, you may ask, how do I hook up the action for the button if I do it this way? You can either provide a delegate method for your cell that calls back to your view controller, or you can pass your cell a block to perform when the button is clicked.
as far as i can get u already have a custom cell.. add a button to existing cell and create an IbOutlet for the same in customCell.h file
then in cellForRowAtIndexPath method access the button using its IBOutlet
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
customCell *cell =(customCell*) [tableView dequeueReusableCellWithIdentifier:#"customCell" forIndexPath:indexPath];
[cell.btn setTitle:#"blabla" forState:UIControlStateNormal];
[cell.btn setTag:indexPath.row];
[cell.btn addTarget:self action:#selector(method:) forControlEvents:UIControlEventTouchUpInside];
if(condition)
{
cell.btn.hidden=YES;
}
else
{
cell.btn.hidden = NO;
}
return cell;
}
Do not forget to register for the customCell class in your ViewController class
[tableName registerNib:[UINib nibWithNibName:#"customCell" bundle:nil] forCellReuseIdentifier:#"customCell"];
you can hide or unhide the button as per your requirements as well as add different actions to the button in each class.
Happy Coding..!!
Do vote it if my code was helpful for you.. ;)

tableViewCell content acts strange

I need some help understanding what may cause my problem in my TableViewCell's content. In my app i use a forum. In the forum we use avatars to the comments/persons. Now I would like to give everyone with name "X" the same avatar. To do so I am trying with the code below but it does not work perfectly and I dont understand why? Sometimes the code bugs and gives for example a person with alias "Y" the "X" alias avatar?
1.First thing I do, is in the storyboard conntect the tag: tableView -> tableViewCell set the UIImageViews tag to 20.
2. Then i run the code below which works in other cases for Alias, Comments in the TableViewCells content.
static NSString *CellIdentifier = #"ForumthreadCell";
UITableViewCell *cell = [pTableView dequeueReusableCellWithIdentifier:CellIdentifier];
ForumPost *x [self.items objectAtIndex:indexPath.row];
UIImageView *pic = (UIImageView *) [cell viewWithTag: 20];
if ([x.alias isEqualsToString:#"X"]){
[pic setImage:[UIImage imagedNamed:#"some picture here.png"]];
}
Any help or suggestions are appreciated. Regards
This is because all the cells in UITableView are reused. That means that your first cell will be reused to load, let's say, your sixth cell. If you don't reload all the elements there and you have image in your first cell, that image will remain in the sixth cell. Another example you could use is to set if indexPath.row == 1 set some property of ForumPost to specific value(after initialization ForumPost *x) and you will have similar behaviour.
To have cells working properly, you need to manage all possible cases.
For instance, you have :
if ([x.alias isEqualsToString:#"X"]){
[pic setImage:[UIImage imagedNamed:#"some picture here.png"]];
}
But you need as well
else if ([x.alias isEqualsToString:#"Y"]){
[pic setImage:[UIImage imagedNamed:#"other pic.png"]];
}
and, if you have other starting names like a, b, c etc.:
else if ([x.alias isEqualsToString:#"Y"]){
[pic setImage:nil];
}

UITableViewCell Custom content re-added when cell becomes visible

I am adding a custom button into my cell.contentView, and I noticed that every time a cell is scrolled off the visible part of the screen and back on, the button gets re-added - the translucent parts of it get more and more solid. What is the correct way to handle it so that it does not keep stacking more objects on top when scrolling through the tableView? Note that custom content is different for each cell, so I cannot put it into the if (cell == nil) {...} block.
The code I have is:
UISegmentedControl *btn = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObject:btn_title]];
// set various other properties of btn
...
[cell.contentView addSubview:btn];
Every time the cell is dequeued, you have to remove the old subviews before adding new ones, or else you'll get that stacking effect. You can do this in one of two places:
a) In tableView:cellForRowAtIndexPath:, remove your old views after the dequeueReusableCellWithIdentifier: call and before adding your new ones.
b) If you're using a subclass of UITableViewCell, you can override prepareForReuse to remove unwanted views. prepareForReuse is called every time a cell is dequeued for reuse, so it's a good place to get rid of old views from the last time the cell was configured.
I'll post a sample fix for the code you posted. It can be extended to take care of more views.
The steps are:
Create a method in your CustomCell class that takes care of the whole setup (for example: setupWithItems:)
Once you have a cell in cellForRowAtIndexPath: (after dequeueing it or creating it), you should call setupWithItems: with the new list of items the cell should display.
In your setupWithItems: implementation, make sure you remove the UISegmentedControl from its parent view. You can easily do this it the segmented control is stored as a property of your custom cell.
In your setupWithItems: implementation, create a new UISegmentedControl and add it to the CustomCell's view hierarchy.
Sample code:
-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
CustomCell* cell = [tableView dequeueReusableCellWithIdentifier:kSomeIdentifier];
if (!cell)
{
// Create a new cell
}
NSArray* currentCellItems = [self cellItemsForRow:indexPath.row];
[cell setupWithItems:currentCellItems];
return cell;
}
And in your CustomCell subclass:
- (void)setupWithItems:(NSArray*)items
{
if (self.segmentedControl)
{
[self.segmentedControl removeFromSuperView];
self.segmentedControl = nil;
}
// More setup...
UISegmentedControl *btn = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObject:btn_title]];
// set various other properties of btn
[cell.contentView addSubview:btn];
}

On iOS, what is the difference between adding a subview to a UITableViewCell object "cell" vs to "cell.contentView"?

In the following code, if we do [cell addSubview: someLabel] vs [cell.contentView addSubview: someLabel], they seem to work the same. Is there any difference doing one or the other? (the custom cell in the real code is adding UIImageView and UILabel) (UIView, on the other hand, doesn't have contentView, so we don't need to add subview to its contentView. UITableViewCell is a subclass of UIView by the way)
-(UITableViewCell *) tableView:(UITableView *) tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
if ([tableView isEqual:self.songsTableView]){
static NSString *TableViewCellIdentifier = #"MyCells";
cell = [tableView dequeueReusableCellWithIdentifier:TableViewCellIdentifier];
if (cell == nil){
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:TableViewCellIdentifier];
}
// ... some code to create a UILabel (not shown here)
[cell addSubview: someLabel]; // vs using [cell.contentView addSubView: ...]
I believe If I am not wrong, the contentView is a subview of UITableViewCell.
If you look at this page here, you can see there are actually 3 subviews in a UITableViewCell
I think by default, the Editing Control is hidden until you enter edit mode for a table in which case, the Editing Control appears (the minus button left of each row) and your contentView gets resized and pushed to the right. This is probably what gives the "proper animation" effect mentioned by the other answer.
To test the difference, try adding a subview such as UILabel with text, to the cell rather than the cell.contentView. When you add it to cell rather than cell.contentView and you enter edit mode for your table, I believe your UILabel will not resize, you will see the edit button ontop/below the minus sign button.
Placing your views in the contentView affects proper animation in and out of edit mode. Place all of your subviews in contentView when you're not subclassing, which should be all of the time unless you know what you're doing.

UITableViewCell's contentView's content not visible when using UISearchDisplayController

I have written a simple contact manager application that uses a UITableView to display the contacts. Each contact is shown as a standard UITableViewCell; custom content is created as UIButtons and UILabels that are added as subviews of the cell's contentView. My table viewController's cellForRowAtIndexPath method includes:
UIButton *emailButton;
UITableViewCell *cell =
[theTableView dequeueReusableCellWithIdentifier:#"My Identifier"];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:identifier] autorelease];
emailButton = [UIButton buttonWithType:UIButtonTypeCustom];
[emailButton setImage:emailImage forState:UIControlStateNormal];
emailButton.tag = EMAIL_BUTTON_TAG;
emailButton.frame = emailButtonFrame;
[cell.contentView addSubview:emailButton];
} else {
emailButton = (UIButton *)[cell viewWithTag:EMAIL_BUTTON_TAG];
}
... set various attributes of the cell, including the content of custom labels
... added as subviews of the contentView exactly as above
This works fine when rendering my table. But I've also added a search bar to my app, set the search bar's controller appropriately, and set the controller's delegate back to this same tableController such that the exact same cellForRowAtIndexPath method is called when performing the searches, and of course I filter the set of cells to be displayed to match the query.
What I see is that when I perform a search, all of the content that I display by setting cell.textLabel.text or cell.imageView.image shows up perfectly in the table, but the emailButton or the labels that I added as subviews of the cell's contentView don't appear. In the debugger, I can clearly see that these controls (the buttons and labels) exist at the time that cellForRowAtIndexPath is called while search filtering is going on. But the controls don't render.
I feel there must be something very subtle in the interactions between table cells and the searchView, but I'm missing it.
Setting the textLabel's text property appears to also bring the textLabel to the front. Even though the text label does not appear to overlap with any of the content view's buttons, this is causing the buttons to disappear. Forcing them to the front after the textLabel is updated makes the problem go away.
It is not clear why this behavior is only appearing in the search case and not in the normal case, but I was able to reproduce it in a simple change to the iOS "TableSearch" example.
you can check if
identifier in *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:identifier] autorelease];
is equalt to "My Identifier" from [theTableView dequeueReusableCellWithIdentifier:#"My Identifier"]; if not you can get empty cell, which you can use it's cell.imageView and cell.textLabel but does not have the contentView subviews.

Resources