Odd behaviour with UITableViewCell when scrolling, UIButtons disappearing - ios

I am trying to set up some UIButtons and UILabels in my custom UITableViewCell. For each row in the table I have 4 buttons with user profile images and below each button is a label displaying the username. Images and usernames are taken from Parse.com. I have a friends array of size 34, and I am displaying 9 rows so the last two buttons and label must be hidden. The code below works but for some reason when I scroll up the table some of the other rows will also hide their two rightmost buttons and labels. I'm wondering if my logic for loading images from the array is incorrect. Not sure what's going on here. Any advice would be greatly appreciated.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
FriendViewCell *cell = (FriendViewCell *)[tableView dequeueReusableCellWithIdentifier:#"friendCell" forIndexPath:indexPath];
for (int i = 0; i < 4; i++) {
if ((int)indexPath.row * 4 + i < [self.currentUser.friends count]) {
UIButton *button = cell.buttons[i];
[button setTag:(int)indexPath.row * 4 + i];
button.layer.cornerRadius = button.frame.size.width / 2;
button.clipsToBounds = YES;
UILabel *label = cell.labels[i];
[button addTarget:self action:#selector(friendTapped:) forControlEvents:UIControlEventTouchUpInside];
//here we need to decide what to access
PFUser *user = self.currentUser.friends[(int)indexPath.row * 4 + i];
label.text = user.username;
PFFile *userImageFile = user[#"profilePic"];
[userImageFile getDataInBackgroundWithBlock: ^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *image = [UIImage imageWithData:imageData];
[button setBackgroundImage:image forState:UIControlStateNormal];
}
}];
}
else {
UIButton *button = cell.buttons[i];
[button setEnabled:NO];
[button setHidden:YES];
UILabel *label = cell.labels[i];
[label setHidden:YES];
}
}
return cell;
}

You can try by unhiding the button and Label in your first conduction like:
if ((int)indexPath.row * 4 + i < [self.currentUser.friends count]) {
[button setEnabled:YES];
[button setHidden:NO];
[label setHidden:NO];
// Other code
}
else{
......
}

The 'iOS' way to handle this would be to fill out the prepareForReuse method of your custom cell. This is where you should be reseting various attributes on cells that may be reused by the caching system.
Apple Docs
-prepareForReuse
Prepares a reusable cell for reuse by the table view's delegate.
Discussion
If a UITableViewCell object is reusable—that is, it has a reuse identifier—this method is invoked just before the object is returned from the UITableView method dequeueReusableCellWithIdentifier:. For performance reasons, you should only reset attributes of the cell that are not related to content, for example, alpha, editing, and selection state. The table view's delegate in tableView:cellForRowAtIndexPath: should always reset all content when reusing a cell. If the cell object does not have an associated reuse identifier, this method is not called. If you override this method, you must be sure to invoke the superclass implementation.

Related

Messing UIButton in each UICollectionViewCell in Scrolling Filmstrip

This is a follow-up question to my previous one and this time I have a problem with UIButton that I have added in each UICollectionViewCell. Before diving into the problem, let me brief out all I've got so far.
Here's the basic rundown of how my UICollectionView in Scrolling Filmstrip style within UITableView works:
Create a normal UITableView with a custom UITableViewCell
Create a custom UIView that will be added to the cell's contentView
The custom UIView will contain a UICollectionView
The custom UIView will be the datasource and delegate for the
UICollectionView and manage the flow layout of the UICollectionView
Use a custom UICollectionViewCell to handle the collection view data
Use NSNotification to notify the master controller's UITableView when
a collection view cell has been selected and load the detail view.
So far, I've been able to add UIButton in each UICollectionViewCell and when I tap the UIButton its image will change to checked mark but the problem occurs when I scroll up/down. Once the cell with checked mark UIButton is off-screen and scrolled back on-screen again, UIButton image starts to get messed around. For example, when UIButton image in cell1 should be checked mark but it's not. Instead checked mark will be appeared in another cell.
Here below is my relevant code:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
PostsCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"PostsCollectionViewCell" forIndexPath:indexPath];
NSDictionary *cellData = [self.collectionData objectAtIndex:[indexPath row]];
cell.postTextLabel.text = [cellData objectForKey:#"text"];
cell.locationNameLabel.text = [cellData objectForKey:#"locationName"];
// >>> Select Button <<<
UIButton *selectButton = [UIButton buttonWithType:UIButtonTypeCustom];
[selectButton setFrame:CGRectMake(110, 150, 20, 20)];
[selectButton setImage:[UIImage imageNamed:#"uncheckedDot.png"] forState:UIControlStateNormal];
[selectButton setImage:[UIImage imageNamed:#"checkedDot.png"] forState:UIControlStateSelected];
[cell.contentView addSubview:selectButton];
[selectButton addTarget:self
action:#selector(buttonPressed:)
forControlEvents:UIControlEventTouchUpInside];
// >>> End Select Button <<<<
return cell;
}
// >>> Select Button Method <<<
-(void) buttonPressed:(UIButton *)sender
{
if([sender isSelected]){
//...
[sender setSelected:NO];
} else {
//...
[sender setSelected:YES];
}
}
Any help would be greatly appreciated! Thanks in advance.
Man, you should be aware that cells (both for UITableView and UICollectionView) are reused via the reuse deque under the hood of UIKit and are not obligated to keep their state consistent with regard to their position in a table view or a collection view. What you need to do is to make sure that the return value of dequeueReusableCellWithReuseIdentifier: is then properly initialized (or, in your case, re-initialized) in accordance with the state of your data model. Basically, you need to store the state of your "checkmarks" (or "checkboxes", what have you) in an array or any other data structure. Then, when you call dequeueReusableCellWithReuseIdentifier:, you should apply that stored state to the return value (the cell you're going to return for the collection view) you just got. Hope it's explained good enough. Good luck.
Maintain in your Data source which cell is to be selected by adding a key for each data source dict. For example:#"isSelected"
Then in cellForRowAtIndexPath:
if ([[cellData valueForKey:#"isSelected"] boolValue]) {
[selectButton setSelected:NO];
} else {
[selectButton setSelected:YES];
}
and in your -(void) buttonPressed:(UIButton *)sender Method:
CGPoint point = [self.collectionView convertPoint:CGPointZero fromView:sender];
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:point];
NSDictionary *cellData = [self.collectionData objectAtIndex:[indexPath row]];
if ([[cellData valueForKey:#"isSelected"] boolValue]) {
[cellData setValue:#"false" forKey:#"isSelected"];
} else {
[cellData setValue:#"true" forKey:#"isSelected"];
}
[self.framesCollectionView reloadItemsAtIndexPaths:#[indexPath]];
AND
In your cellForRowAtIndexPath set a tag for the UIButton. Then before adding a UIButton as subview to the cell's contentView check if a view with this tag already exists. In case the cell has been reused, just use the already existing button.

uitableview cells display wrong data when off screen at loading time

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

Some rows never have UIButtons when UITableView is created

I have a UITableView that is never consistent. This table view has 2 sections.
I have created 2 UIButtons, they both are practically the same image, but one is grey and one is red.
Every row in the UITableView is supposed to have the grey button when the UITableView first loads, and then if a user taps a button then it switches to the red version of the button.
For some reason, about 80% of the time when I load the view controller with this UITableView on my iPhone, the first 2 rows in the first section will not have a button. What's weird is if I tap my finger where the button is supposed to be 2 things happen:
The functionality attached to the button works and NSLogs in the console in xcode.
The red button appears.
So it's like the grey button is there, it's just invisible. This doesn't work because if a user sees 2 rows without a button they are going to think something is broken or that they can't tap on that row.
Also, if I scroll to the very bottom of my UITableView, and then scroll back up again, the invisible grey buttons will magically appear like they were there the whole time.
Here are the first 3 method implementations that are involved in creating my UITableView:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2 ;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if(section == 0)
return [self.potentiaFriendsInParseFirstNamesArray count];
if(section == 1)
return [self.potentiaFriendsNotInParseFirstNamesArray count];
else return 0;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if(section == 0)
return #"Friends Using The App";
if(section == 1)
return #"Send to Contact";
else return #"nil";
}
And then here is my cellForRowAtIndexPath method implementation which does most of the work in creating the UITableView and which actually creates the UIButton settings for each row. The if else statement really has 2 sets of nearly identical code, the only difference is one is for section 1 of the table view and the other is for section 2:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"SettingsCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (indexPath.section == 0) {
NSString *firstNameForTableView = [self.potentiaFriendsInParseFirstNamesArray objectAtIndex:indexPath.row];
PFUser *user = [self.allUsersInParse objectAtIndex:indexPath.row];
UIImage *addUserButtonImage = [UIImage imageNamed:#"SliderThumb-Normal-g"];
UIImage *addUserButtonImageHighlighted = [UIImage imageNamed:#"SliderThumb-Normal"];
UIButton *addUserButton = [[UIButton alloc]init];
addUserButton.frame = CGRectMake(237, -10, 64, 64);
[addUserButton setImage:addUserButtonImage forState:UIControlStateNormal];
[addUserButton setImage:addUserButtonImageHighlighted forState:UIControlStateHighlighted];
[addUserButton setImage:addUserButtonImageHighlighted forState:UIControlStateSelected];
[addUserButton addTarget:self action:#selector(handleTouchUpInside:) forControlEvents:UIControlEventTouchUpInside];
addUserButton.tag = indexPath.row;
[cell.textLabel setText:firstNameForTableView];
[cell.detailTextLabel setText:user.username];
[cell.contentView addSubview:addUserButton];
} else {
NSString *firstNameForTableView2 = [self.potentiaFriendsNotInParseFirstNamesArray objectAtIndex:indexPath.row];
NSString *userNameForTableView2 = [self.potentiaFriendsPhoneNumberArray objectAtIndex:indexPath.row];
UIImage *addFriendButtonImage = [UIImage imageNamed:#"SliderThumb-Normal-G"];
UIImage *addFriendButtonImageHighlighted = [UIImage imageNamed:#"SliderThumb-Normal"];
UIButton *addFriendButton = [[UIButton alloc]init];
addFriendButton.frame = CGRectMake(237, -10, 64, 64);
[addFriendButton setImage:addFriendButtonImage forState:UIControlStateNormal];
[addFriendButton setImage:addFriendButtonImageHighlighted forState:UIControlStateHighlighted];
[addFriendButton setImage:addFriendButtonImageHighlighted forState:UIControlStateSelected];
[addFriendButton addTarget:self action:#selector(handleTouchUpInsideForNonUsers:) forControlEvents:UIControlEventTouchUpInside];
addFriendButton.tag = indexPath.row;
[cell.textLabel setText:firstNameForTableView2];
[cell.detailTextLabel setText:userNameForTableView2];
[cell.contentView addSubview:addFriendButton];
}
return cell;
}
I have not included the full method implementations that these 2 statements trigger because they only handle the functionality attached to each button, and most likely have nothing to do with this issue, but I will post them here anyways. These 2 statements can be found in the if else for cellForRowAtIndexPath and handle the touch events for the 2 sections of the table view:
[addFriendButton addTarget:self action:#selector(handleTouchUpInsideForNonUsers:) forControlEvents:UIControlEventTouchUpInside];
[addFriendButton addTarget:self action:#selector(handleTouchUpInside:) forControlEvents:UIControlEventTouchUpInside];
I see the pattern of trying to set up a custom UITableViewCell from within tableView:cellForRowAtIndexPath: quite a lot. The trouble is that I believe it to be the wrong place to do it. If you're really only using a built-in UITableViewCell, then it's the right place; for custom cells, it's not.
I'd recommend making a UITableViewCell subclass with a "setup" method that takes an object and uses that object to set up the cell's UI.
For instance, take section 0 from your UITableView. I'd do something like this:
#interface UserTableViewCell : UITableViewCell
// properties to IBOutlet connections for UI setup
- (void)setupWithUser:(PFUser *)user;
#end
#implementation UserTableViewCell
- (void)setupWithUser:(PFUser *)user {
// Set up your UI with properties from 'user'
}
- (IBAction)buttonPressed:(UIButton *)sender {
// Respond to the button press
}
This way, you can set up the cell's layout as well as images for the button in IB, rather than code. This also makes the code a lot easier to read (and re-read 6 months later!).
Also, unless the 2 cells (section 0 and section 1) are really interchangeable, I'd recommend two different subclasses of UITableViewCell to give you greater control, rather than trying to force one to act like the other or vice versa. Then, you simply assign different identifiers and recycle the appropriate type.

Make TableView buttons take up less memory

I have code here that generates a list of cells that contain buttons.
I'm pretty sure there is a way to make it so that I am creating 1 button object instead of 20, but I'm not quite sure how to implement that?
I need some guidance as to where to make it so I only create 1 button object.
Additionally, I might be making 20 cell objects when I only need 1 as well?
I don't totally understand how to utilize memory well yet.
My code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"TimesCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//[cell sizeToFit];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
if([cell.contentView viewWithTag:3] != nil){
[[cell.contentView viewWithTag:3] removeFromSuperview];
}
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self
action:#selector(customActionPressed:)
forControlEvents:UIControlEventTouchDown];
[button setTitleEdgeInsets:UIEdgeInsetsMake(-10.0f, 0.0f, 0.0f, 0.0f)];
button.frame = CGRectMake(5, 5, 310, 50);
button.tag = 3;
int rows = [yourDefaultActivities count];
int rowsl = rows - 1;
float r = 1+(((255-1+75)/rowsl)*(indexPath.row));
if(r > 255){
r = 255;
}
float g = 255-(((255-1+75)/rowsl)*(indexPath.row));
if(g < 0){
g = 255-fabsf(g);
}else{
g = 255;
}
float b = 1;
UIColor *thisColor = [UIColor colorWithRed:r/255 green:g/255 blue:b/255 alpha:1];
button.backgroundColor = thisColor;
button.clipsToBounds = YES;
button.layer.cornerRadius = 15;
button.titleLabel.font = [UIFont fontWithName:#"HelveticaNeue" size:14];
[button setTitle:[[yourDefaultActivities objectAtIndex:indexPath.row] objectAtIndex:0] forState:UIControlStateNormal];
[button.titleLabel setTextColor:[UIColor blackColor]];
[button.titleLabel setFont:[UIFont fontWithName:#"HelveticaNeue" size:14]];
[cell addSubview:button];
return cell;
}
EDIT: I tried to remove the buttons every time I created a new one, but I must be doing it wrong..
One button object can't appear in 8 cells at once -- you do need as many cells and buttons as will fit on the screen at any time (it looks like 8 or 9). But 8 or 9 cell with buttons in them should not cause any memory problems -- buttons aren't that expensive. The problem with the code you posted though, is that it is adding buttons every time cellForRowAtIndexPath is called, which happens a lot when you scroll. Since cells are reused, you don't want to be adding a button to a cell that already has one. The easiest solution, in my opinion, is to make a custom cell in the storyboard, and add you buttons there. You can still set their color in code based on the indexPath so you get the look you want. Alternately, you can check whether the cell (actually the cell.contentView which is where you should be adding them, not directly to the cell) has a subview of class UIButton, and only add a button if it doesn't already have one.
After Edit:
In answer to your comment, you don't need to make the rounded button in the storyboard, you can just add a button (type custom) to your subclassed cell, and then modify its look in code. Here is an example of what I mean. In this test app, I created a custom cell class (RDCell) and changed the class of the cell in the storyboard to that. I added a custom button to the cell, positioned and sized it with constraints, and made an IBOutlet to it in RDCell.h. Here is what I have in the table view controller:
#import "TableController.h"
#import "RDCell.h"
#implementation TableController
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 20;
}
-(void)tableView:(UITableView *)tableView willDisplayCell:(RDCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
cell.button.backgroundColor = [UIColor colorWithHue:.15 + (indexPath.row/30.0) saturation:1 brightness:1 alpha:1];
cell.button.layer.cornerRadius = 15;
cell.button.titleLabel.textColor = [UIColor blackColor];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
RDCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
[cell.button addTarget:self action:#selector(customActionPressed:) forControlEvents:UIControlEventTouchUpInside];
cell.button.tag = indexPath.row;
return cell;
}
-(void)customActionPressed:(UIButton *) sender {
NSLog(#"button pressed in row: %d",sender.tag);
[self.tableView reloadRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:sender.tag inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];
}
This was the resultant view:
I put a log in initWithCoder for RDCell, and it's called only 12 times (the max. number of cells on the screen at any one time), so there's never more than 12 cells or 12 buttons.

Losing UIButton within a custom cell when reloading table

I've custom UITableViewCells where I placed buttons dynamically in code. I have programmatically set actions for such buttons, and when the action is triggered, then I need to reload the table, among other things. Once reload is called, the sender button disappears, and I don't know why... Could somebody give me a hint for the possible reason? If button is not tapped, and I scroll up and down through the table, it remains there.
Thanks!
EDIT
This is a code snippet from cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSArray *listData =[self.tableViewEntries objectForKey:[self.sortedKeys objectAtIndex:[indexPath section]]];
UITableViewCell *cell;
if (indexPath.section == 0) {
switch (indexPath.row) {
case 0: {
cell = [self.tableView dequeueReusableCellWithIdentifier:#"firstCell"];
((CustomCell *)cell).cellTextField.tag = firstCellTag;
cell.tag = firstCellTag;
break;
}
case 1: {
cell = [self.tableView dequeueReusableCellWithIdentifier:#"secondCell"];
((CustomCell *)cell).cellTextField.tag = secondCellTag;
cell.tag = secondCellTag;
break;
}
}
return cell;
}
The CustomCell I've defined is loaded from a nib. Those cells have a UITextField for user input and a UIView where I set or remove the buttons dynamically in code according to the user input within the text field. The buttons are created in code, and their actions are set in code as well:
- (void)setButtonForCell:(UITableViewCell *)cell withResult:(BOOL)isValid
{
UIImage* validationButtonNormalImage = nil;
UIImage* validationButtonHighlightImage = nil;
UIButton *validationButton = [UIButton buttonWithType:UIButtonTypeCustom];
[validationButton setFrame:CGRectMake(0, 0, ((CustomCell *)cell).cellValidationView.frame.size.width, ((CustomCell *)cell).cellValidationView.frame.size.height)];
if (isValid) {
validationButtonNormalImage =[[UIImage imageNamed:#"success.png"] stretchableImageWithLeftCapWidth:5.0 topCapHeight:0.0];
validationButtonHighlightImage =[[UIImage imageNamed:#"success.png"] stretchableImageWithLeftCapWidth:5.0 topCapHeight:0.0];
[validationButton removeTarget:self
action:#selector(validationButtonTapped:)
forControlEvents:UIControlEventTouchUpInside];
}
else {
validationButtonNormalImage =[[UIImage imageNamed:#"error.png"] stretchableImageWithLeftCapWidth:5.0 topCapHeight:0.0];
validationButtonHighlightImage =[[UIImage imageNamed:#"error.png"] stretchableImageWithLeftCapWidth:5.0 topCapHeight:0.0];
[validationButton setTag:cell.tag];
[validationButton removeTarget:self
action:#selector(validationButtonTapped:)
forControlEvents:UIControlEventTouchUpInside];
[validationButton addTarget:self
action:#selector(validationButtonTapped:)
forControlEvents:UIControlEventTouchUpInside];
}
[validationButton setBackgroundImage:validationButtonNormalImage forState:UIControlStateNormal];
[validationButton setBackgroundImage:validationButtonHighlightImage forState:UIControlStateHighlighted];
[((CustomCell *)cell).cellValidationView.subviews makeObjectsPerformSelector: #selector(removeFromSuperview)];
[((CustomCell *)cell).cellValidationView addSubview:validationButton];
}
Then, in validationButtonTapped:, I do some tasks and call to [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:NO];, and there is when I loose the button from the view. However, if I don`t tap the button and I scroll down and up again to get 'cellForRowAtIndexPath:' called again, the button remains here. It is like I only loose it when calling for a reload.
Any help?
Cells are usually reused if you are following the usual cellForRowAtIndexPath: pattern. Do you remove all subviews in prepareForReuse for each cell? If you didn't then you would see unexpected buttons on cells as you scroll up and down. You probably need to add the buttons as required in each cell's configuration after you get the cell for reuse (or in newer code, dequeue a cell) in cellForRowAtIndexPath:.

Resources