I recently battled with this feature: to create a toggling checkmark on a UITableViewCell, between two different cells. Here is the solution, in brief.
The tableview loads from an array that contains arrays- each array is a section. The one in question contains two values – the option values, @”A” and @”B”. We’ll call it optionArray.
We will create a dictionary that holds the user’s last selected values.
optionArray = [[NSArray alloc] initWithObjects:@”A”, @”B”,nil];
selectionDictionary = [[NSMutableDictionary alloc] initWithCapacity:1];
[selectionDictionary setObject:@”A” forKey:@”option1″];
I’m loading the tableview from an object called “sectionArray”, which contains a single array, the optionArray. I also have an array called “secitonTitleArray”, which just includes labels for the table. You probably are loading your table from Core Data or another data system. The way I’m doing it isn’t recommended- it’s simply for the example.
sectionArray = [[NSArray alloc] initWithObjects:optionArray], [nil];
sectionTitleArray = [[NSarray alloc] initWithObjects:@"Option1"];
- First, let’s draw the table. In the “CellForRowAtIndexPath” method, check your datasource and add the checkmark in the valid (default or stored) value:
NSString *selectedValueString = [selectionDictionary objectForKey:@"option1"];
cell.textLabel.text =selectedValueString;
- We’ll use a simple if statement to determine if this is the user’s stored, selected object, and draw the accessory type, if so. It’s easier to compare numbers (vs. strings) so let’s get the index of the selected string in the optionArray, and compare it to the one being drawn (indexpath.row)
NSInteger selectionIndex = [optionArray indexOfObject:selectedValueStr];
- So for @”A”, since it is the first element in the array, that’s 0. If we are drawing the A cell, this will qualify. Draw the accessory type:
if( selectionIndex == indexPath.row ){
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
OK, that’s great for drawing the checkmark on the right cell. Let’s do the interactive bit now- toggling from cell to cell based on the user’s touch.
- In the tableview’s delegate methods– “didSelectRowAtIndexPath”– you will various cases. Since this is interpreted each time, for each cell, we need to create various cases. Since we’re managing comparisons more easily with integers, let’s get the relevant indexpath.rows/indexes, of each object:
NSInteger selectedValueIndex = [optionArray indexofObject:selectedValueString];
// note, that is the index in the optionArray. There is another index - what the user has selected, that is passed as an argument in this method - "indexPath.row".
- if selecting already selected cell, return, i.e. do nothing
if (selectedValueIndex == indexPath.row) {
return;
}
- Since we’ve passed this point, we know that what they’ve selected was the old selected cell. So indexpath.row is essentially not going to be the checked value (“new”) in the end state of this routine. Sorry to berate this issue, but the use of “new and old” in most posted examples confused me. Old means, I believe, “was checked but won’t be based on the user’s interaction.”
NSIndexPath *oldIndexPath = [NSIndexPath indexPathForRow:selectedValueIndex inSection:indexPath.section];
Remember the section– that was my problem for a bit. It is important when you’re grabbing indexPaths from the aTableView array to tell it which sub-array (section) to look at. - We’ve figured out which is the old and which is the new. So to continue, we need to create cell objects, so we can access the accessoryTypes (the checkmark).
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:oldIndexPath];
- Now, we are going to draw the carets to the cell. This is pretty clear – if it’s checked, it’s no longer checked. If it wasn’t checked, it is now.
if (newCell.accessoryType == UITableViewCellAccessoryNone) {
newCell.accessoryType = UITableViewCellAccessoryCheckmark;
[sectionArray setObject:newCell.textLabel.text forKey:@"option1"];
}if (oldCell.accessoryType == UITableViewCellAccessoryCheckmark) {
oldCell.accessoryType = UITableViewCellAccessoryNone;
}
- You may have noticed an assignment above. In the “if-checked-switch-to-unchecked” if statement, we have updated our tableView’s datasource with the user’s choice. It’s interesting that the UIKit’s attributes don’t inform the datasource.
- Not done yet! Add “[aTableView reloadData] to the ViewDidAppear and ViewDidLoad, to reflect the user’s changes.
- if selecting already selected cell, return, i.e. do nothing