5
votes

When i select a player in 'didSelectRowAtIndexPath' and add a checkmark on the selected row it adds an additional checkmark.

If i tap row = 0 it adds a checkmark to row = 0 and row = 11. This means that two row's are marked by one tap. If i tap row = 1 it adds an extra checkmark to row = 10 so it adds checkmark 10 rows forward. It seems like it only add the checkmark as the player does not get into the actual player-list.

Any help would be very much appreciated.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

NSLog(@"indexPath: %i", indexPath.row);

// To many players selected
if (nrOfSelectedPlayers == 6) { //This is max players allowed
    UIAlertView *alertPlayer = [[UIAlertView alloc] initWithTitle:@"VARNING"
                                                          message:@"Du kan maximalt spela \n med sex spelare!" 
                                                         delegate:self 
                                                cancelButtonTitle:@"Tillbaka" 
                                                otherButtonTitles:nil];

    [alertPlayer show];
    [alertPlayer release];
    nrOfSelectedPlayers--;
    checkDeletePlayer = YES;
}
else { 

    // Handle the number of selected players to be able to delete player +6
    if (checkDeletePlayer == YES) {
        checkDeletePlayer = NO;
        nrOfSelectedPlayers++;
    }


    if (cell.accessoryType == UITableViewCellAccessoryNone) {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
        [selectedPlayersArray addObject:cell.textLabel.text];
        nrOfSelectedPlayers++;
    } 
    else {
        cell.accessoryType = UITableViewCellAccessoryNone;
        selectedPlayer = cell.textLabel.text;

        for (int oo = 0; oo < nrOfSelectedPlayers; oo++) {
            if ([selectedPlayersArray objectAtIndex:oo] == cell.textLabel.text) {
                [selectedPlayersArray removeObjectAtIndex:oo];
                nrOfSelectedPlayers--;
            }
        }
        //nrOfSelectedPlayers--;
    }
}
}
3
thx dude your question and your code snippet saved me asking another duplicate question!codejunkie

3 Answers

7
votes

I am using storyboards with Dynamic Prototype Cells to display a list of states I used some of the ideas above before I found this solution

Step 1

@interface StateViewController : UITableViewController
{
    NSMutableArray *checkedIndexPaths;
}

Step 2

(void)viewDidLoad
{
    [super viewDidLoad];
    self.states = [[GAIGStateStore sharedInstance]allStates];

    //Setup default array to keep track of the checkmarks
    checkedIndexPaths = [NSMutableArray arrayWithCapacity:self.states.count];
    for (int i = 0; i < self.states.count; i++) {
        [checkedIndexPaths addObject:[NSNumber numberWithBool:NO]];
    }
}

Step 3

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{

    //This toggles the checkmark
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

    if (cell.accessoryType == UITableViewCellAccessoryNone)
    {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
        //This sets the array
        [checkedIndexPaths replaceObjectAtIndex:indexPath.row withObject:[NSNumber numberWithBool:YES]];

    } else
    {
        cell.accessoryType = UITableViewCellAccessoryNone;
         //This sets the array
        [checkedIndexPaths replaceObjectAtIndex:indexPath.row withObject:[NSNumber numberWithBool:NO]];

    }


}

Step 4

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:stateCell forIndexPath:indexPath];

    UILabel *stateLabel = (UILabel *)[cell viewWithTag:1000];

    StateProvince *myState = [self.states objectAtIndex:indexPath.row];

    stateLabel.text = myState.label;

    //Now set the check marks
    // Assume cell not checked;
    [cell setAccessoryType:UITableViewCellAccessoryNone];

    NSNumber *num = [checkedIndexPaths objectAtIndex:indexPath.row];


    if (num == [NSNumber numberWithBool:YES]) {
           [cell setAccessoryType:UITableViewCellAccessoryCheckmark];
    }


    return cell;
}
6
votes

The problem you are facing is caused by cell reusing.

Basically, if your UITableView has, let say 50 cells to display, it creates only 10 and then reuse them as you scroll down / scroll up. So whatever changes you did to the cell at row 0, it will be re-displayed for the row 11 as TableView uses the same cell etc.

What you want to do is to keep track of which players have been selected independently from cell. You can achieve that easily by creating a collection, let say NSMutableArray or NSMutableDictionary, which will store BOOL values in NSNumber objects, eg.

NSMutableArray *players = [NSMutableArray arrayWithCapacity:50];
for (int i = 0; i < 50; i++) {
    [players addObject:[NSNumber numberWithBool:NO]];
}

Then in didSelectRowAtIndexPath:(NSIndexPath *)indexPath you do instead of operating on cell, you will simply change the value of a corresponding NSNumber object.

Then in cellForRowAtIndexPath:(NSIndexPath *)indexPath you configure cell accessory by checking the corresponding entry in players collection.

Or if you are really, really stubborn you could replace (THIS IS NOT RECOMENDED) the following line from the cellForRowAtIndexPath:(NSIndexPath *)indexPath:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

with:

UITableViewCell *cell = nil;
2
votes

For others coming here (like I did) to see why their table selects random cells, you have to add something like the following to your cellForRowAtIndexPath:

// Assume cell not checked;
[cell setAccessoryType:UITableViewCellAccessoryNone];
for (int i = 0; i < checkedIndexPaths.count; i++) {
    NSUInteger num = [[checkedIndexPaths objectAtIndex:i] row];

    if (num == indexPath.row) {
        [cell setAccessoryType:UITableViewCellAccessoryCheckmark];
    }
}

I keep a NSMutableArray called checkedIndexPaths so that I know which indexPaths are checked. Keeping such an array allows you to easily limit the number of cells a user can check. Here's an example of my didSelectRowAtIndexPath:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

    // uncheck if already checked
    if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
        cell.accessoryType = UITableViewCellAccessoryNone;
        [checkedIndexPaths removeObject:indexPath];
    }
    else {
        // make sure no more than 3 are selected
        if ([checkedIndexPaths count] < 3) {

            // check row
            cell.accessoryType = UITableViewCellAccessoryCheckmark;
            // add it to our list of checked indexes
            [checkedIndexPaths addObject:indexPath];
        } else {
            UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Note" 
                                                            message:@"You can only select 3 rows." 
                                                           delegate:nil 
                                                  cancelButtonTitle:@"OK" otherButtonTitles:nil];
            [alert show];
        }
    }  
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}