Need logic for UITableView's checkmark accessory - iphone

I have 5 cells in my tableview. User can select a row which sets the cells' accessory to check marked. They can select multiple rows.
I want to make sure one cell is always check marked. How can I do this? All rows are check marked by default.
Here is the code I use in didSelectRow
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
[self.tableView deselectRowAtIndexPath:iIndexPath animated:YES];
}

Implement tableView:willDeselectRowAtIndexPath:. If there is only one element selected, return nil to indicate that the table may not deselect it.

Related

multiple selection in grouped tableview

I am working with grouped tableview with multiple sections.
and I have to implement the functionality of multiple selection on didselectrow at indexpath
method. my code is as follows.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)path
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:path];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark)
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
else
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
which allows me to select multiple cells
but when I scroll my tableview at that time my selection disappears.
Your selection goes off when you scroll because it calls cellForRowAtIndexPath and there you have not handle selection.
To avoid this problem you can do as follows:
In didSelectRowAtIndexPath you can save index path of selected row as follows:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)path
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:path];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark)
{
cell.accessoryType = UITableViewCellAccessoryNone;
//remove index path
[selectedIndexPathArray removeObject:path];
}
else
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[selectedIndexPathArray addObject:path];
}
}
and in cellForRowAtIndexPath you can check whether cell is selected or not.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//If selectedIndexPathArray contains current index path then display checkmark.
if([selectedIndexPathArray containsObject:indexPath])
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
Try this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
if (self.tableView.isEditing) {
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
} else {
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
return cell;
}
-(UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewCellEditingStyleMultiSelect;
}
-(IBAction) switchEditing {
[self.tableView setEditing:![self.tableView isEditing]];
[self.tableView reloadData]; // force reload to reset selection style
}
Hope this helps solving your problem.(ref)
Your selection disapear cause the method CellForRowAtIndexPath will be called at scroll.
You need to set the accessory again.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
// here
...
}
You are getting this issue because you are not tracking your row selections.When the callback method cellForRowAtIndexPath gets called for the rows that had disappeared/(scrolled up/down) the cell object no longer remembers whether it was selected or not. (reason those selections are disappearing)
I would suggest you to use a collection like NSMutableArray/NSArray to track the selected rows.
You can use either of these approaches.
This would be a quick working fix:
Add/Remove the index path object in didSelectRowAtIndexPath based on the users selection
and then based on the contents of that array u can toggle the value of cell.accessoryType for the corresponding cell.
Ideally,you can use a data bean/model with some boolean member called selected and u can update its value based on the selection made.Then instead of simply adding those index path u can add those meaningful data bean objects onto your array and get the selections back from the selected property of the bean.This approach would help u in getting back the row selections even if the user kills and restarts the app provided u persist the bean objects in database/archive..(But it all depends on ur use case and requirements!)
Hope this helped!

UITableView moves cell contents around when scrolling

I have seen other questions on here related to this, but I have their solutions in place, and things still move around when I scroll.
Basically, I have a table of data and I want the first and only the first row to have a UITableViewCellAccessoryDisclosureIndicator. The problem is, when I scroll around, the indicator duplicates, deletes, and moves to other cells. Here is my code..
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if(indexPath.row == 0) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
cell.textLabel.text = [tabledata objectAtIndex:indexPath.row];
return cell;
}
The problem is something with the indexPath.row variable.. its somehow returning 0 in other cells that aren't the very first (when it comes to displaying cells). HOWEVER... the data is always right from my array(which implies the value HAS to be correct) and in my
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
method, i only wish to change screens when row 0 is clicked.. and regardless of where I have scrolled to, no cells trigger except the very first. So it seems THAT check is always correct while the one inside cellForRowAtIndexPath is not...
Most likely, you are re-using cells that already have the accessoryType set. You need to explicitly set it to NOT show on cells where it shouldn't.
if(indexPath.row == 0){
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}

UITableCell Reusing itself

I made a UITableViewCell and I have 20 rows in that table in which 5 at a time on screen.
I have set accessory view checkd mark in didSelect delegate method on which row is being selected. My concern is let suppose first row is selected and its accessory type is checked, now if i scroll the table we see the sixth one is also checked. I know that the cell is reusing itself and not creating itself again.
The model should be able to handle which cell is checked and which is not. To simplify the problem, you can keep an array which will the NSIndexPaths that should be checked. If only one can be checked at the time, an ivar of the type NSIndexPath is more than enough.
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([[aTableView cellForRowAtIndexPath:indexPath] accessoryType] == UITableViewCellAccessoryCheckmark)
{
// Ok this one is selected, so we will remove it from the Reference Array.
}
else
{
// Ok this one doesn't has a checkMark
// First add the checkmark
[[aTableView cellForRowAtIndexPath:indexPath] setAccessoryType:UITableViewCellAccessoryCheckmark];
// Add the NSIndexPath to the Array of references
}
}
In the delegate method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *cellId = [NSString stringWithFormat:#"cell%d",indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId ];
if (cell==nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:cellId ] autorelease];
}
}
Set different cell ID to different cell row.

how to set accessoryType when cell has been selected

I have been trying to figure out how to set the accessoryType to UITableViewCellAccessoryCheckmark when the cell is selected but am having trouble finding a decent example of this.
If you know how to do this or a good tutorial could you please let me know that would be great.
To restrict the user to just one selection, meaning to create an exclusive list of one choice only, you could follow these steps;
Firstly, have a global index path declared in your .h file to keep track of the already selected cell ->
NSIndexPath *oldIndexPath;
When you create the cells, be sure to set the accessory type to none, so that no cell is selected by default when the table is seen;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellIdentifier"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"CallIdentifier"];
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
Finally, in the didSelectRowAtIndexPath delegate method, add the following code which will remove the checkmark from the already selected cell, and add a checkmark to the newly selected one.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (oldIndexPath==nil) { // No selection made yet
oldIndexPath=indexPath;
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
}
else {
UITableViewCell *formerSelectedcell = [tableView cellForRowAtIndexPath:oldIndexPath]; // finding the already selected cell
[formerSelectedcell setAccessoryType:UITableViewCellAccessoryNone];
[cell setAccessoryType:UITableViewCellAccessoryCheckmark]; // 'select' the new cell
oldIndexPath=indexPath;
}
}
Hope this works out! :)
Something like this may work:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self tableView:myTableView cellForRowAtIndexPath:indexPath];
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
}
To answer the comment below, just push a viewController in the same method like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self tableView:myTableView cellForRowAtIndexPath:indexPath];
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
// Then push a new view
iPhoneCustomViewController *myVC = [[iPhoneCustomViewController alloc] initWithNibName:#"iPhoneCustomViewController" bundle:nil];
[self.navigationController pushViewController:myVC animated:YES];
[myVC release];
// And deselect the row (if desired)
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
Did you know that:
1.) UITableView keeps track of the index paths for the rows that have been selected? It's in an array called indexPathsForSelectedRows
2.) UITableView has a flag you can set to make it either single or multiple selection. You can change it by calling the setter setAllowsMultipleSelection:(BOOL).
So, assuming that the table has been set to single selection, we can do the following in the tableView:CellForRowAtIndexPath method ...
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *simpleTableIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[[cell textLabel] setText:#"Some Text"];
NSArray *selectedIndexPaths = [tableView indexPathsForSelectedRows];
if ([selectedIndexPaths containsObject:indexPath]) {
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
}else{
[cell setAccessoryType:UITableViewCellAccessoryNone];
}
return cell;}
This implementation of CellForRowAtIndexPath will give you a clean checkmark with no gray background when a cell is selected. You will need to set the checkmark in the didSelectRowAtIndexPath delegate method to make sure a cell gets the checkmark the moment it gets selected.
No need to create separate ivars or anything else to keep track of what was or wasn't selected. It's all neatly contained in the UITableView as Apple intended.
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
newCell.accessoryType=UITableViewCellAccessoryCheckmark;
Implement this in didSelectRowAtIndexPath delegate method
From the docs:
The delegate handles selections in this method. One of the things it
can do is exclusively assign the check-mark image
(UITableViewCellAccessoryCheckmark) to one row in a section
(radio-list style). This method isn’t called when the editing property
of the table is set to YES (that is, the table view is in editing
mode). See "Managing Selections" in Table View Programming Guide for
iOS for further information (and code examples) related to this
method.
Here is an example:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]
cell.UITableViewAccessoryType = UITableViewCellAccessoryCheckmark;
}

Problem with configuring a UITableView as a inclusive selection list

I am programing a UITableView to behave as an inclusive selection list. My table displays correctly and allows for multiple cells to be selected with check boxes. My problem is that cells which have been selected (cells contain a check mark to the right) loose their selected status when scrolled out of view (cells check mark disappears). I want the selections made to cells in the table to be preserved even if cells are scrolled out of view. Does anyone have any idea what is causing this?
Here is my code inside of my TableViewController class:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSUInteger row = [indexPath row];
cell.textLabel.text = [widgetTitles_glob objectAtIndex:row];
cell.detailTextLabel.text = #"";
cell.textLabel.textColor = [UIColor blackColor];
cell.textLabel.font = [UIFont boldSystemFontOfSize:15];
cell.accessoryType = UITableViewCellAccessoryNone;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:[tableView indexPathForSelectedRow] animated:YES];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
// Reflect selection in data model
} else if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
// Reflect deselection in data model
}
}
Any help would be very much appreciated.
When you are using UITableView correctly, only as many UITableViewCell instances are allocated as are needed to fit on the screen. When you scroll down a table, and a cell disappears off the top of the screen, it is relocated to the bottom.
Your delegate method, tableView:cellForRowAtIndexPath: is responsible for setting up a cell, either creating a new one or reconfiguring a recycled one.
The proper thing to do is use an array to store your checked/unchecked values. When didSelectRowAtIndexPath: is called, you update the cell and your array. When tableView:cellForRowAtIndexPath: is called, you configure the cell based on the values in the array.
Based on your comments, you are already doing the right thing in didSelectRowAtIndexPath:; you just need to use those values when you set up the cell instance, because that cell could represent a row that has already been checked. Check the array and then set cell.accessoryType accordingly.
In cellForRowAtIndexPath: you were assigining the accessoryType as none, so whenever you scroll that delegate is called and set the accessory type as none. So you should change your code.
I have also faced this problem once; I came up with a solution as follows.
Store the indexPath.row values of selected indexPath in an array (this code should be in didSelectRowAtIndexPath delegate) if it is deselected remove from that array. In cellForRowAtIndexPath: method I have used a for loop and check if that indexPath.row is present then change it's accessory type to checkmark else none.
Thanks for your help. It actually turns out that the reason why the cells were getting reset to UITableViewCellAccessoryNone was becasue of the following line of code inside of cellForRowAtindexPath:
cell.accessoryType = UITableViewCellAccessoryNone;
Removing this has fixed the table.