UITableView won't uncheck the first selected row - ios

I have a UITableView where the user should be able to select (check) multiple rows.
I have an NSMutableArray in my controller to hold the selected items, and in my cellForRowAtIndexPath method, I check whether the item is in that array and return the cell in a checked/unchecked state accordingly.
Here's the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = kContactCellReuseIdentifier;
static NSString *searchIdentifier = kContactSearchCellReuseIdentifier;
POContactCell *cell;
// Configure the cell...
if (tableView == self.tableView) {
cell = (POContactCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.contact = self.contacts[indexPath.row];
NSLog(#"Returned cell with name %#", cell.contact.name);
} else {
cell = (POContactCell*)[tableView dequeueReusableCellWithIdentifier:searchIdentifier forIndexPath:indexPath];
cell.contact = self.searchResults[indexPath.row];
}
if ([self.selectedContacts containsObject:cell.contact])
{
NSLog(#"was checked");
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
cell.accessoryType = UITableViewCellAccessoryNone;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
POContactCell* tappedCell = (POContactCell*)[self tableView:tableView cellForRowAtIndexPath:indexPath];
NSLog(#"Selected contact %#", tappedCell.contact.name);
if ([self.selectedContacts containsObject:tappedCell.contact]) {
// cell is already selected, so deselect it
NSLog(#"It's already selected, so deselect it");
[self.selectedContacts removeObject:tappedCell.contact];
tappedCell.accessoryType = UITableViewCellAccessoryNone;
}
else
{
NSLog(#"It's not already selected, so select it");
[self.selectedContacts addObject:tappedCell.contact];
tappedCell.accessoryType = UITableViewCellAccessoryCheckmark;
}
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:NO];
}
This code works... except for the first selection. The first cell that the user taps will get checked and will never get unchecked. I see from the log statements that all the cells are going through the exact same process and it's correctly recognizing the selection state of the first tapped row too, even though the accessory view doesn't reflect it.
After the first selection, all the other rows work perfectly.
Any debugging ideas?

You should be putting self.contacts[indexPath.row] (or self.searchResults[indexPath.row], as appropriate) in your array of selected items, and checking whether or not those objects exist or not in the array when the user taps a cell. You are almost doing that, it would appear, by setting cell.contact to the object from your data source and checking for cell.contact in your array. But I'd try putting the object directly into your array, e.g.
id contact = self.contacts[indexPath.row];
if ([self.selectedContacs containsObject:contact])
...
and stop checking if cell.contact is in the array to determine "selected-ness".
In a UITableView there is a small set of actual UITableViewCell objects in memory, and they get re-used. The root of your problem could very well be this, because you are checking to see if cell.contact is in your set of selected items; when a cell is reused, unless you wrote your own prepareForReuse, the previous value of your custom attributes may not (likely will not) be cleared.
Does that make sense?

Related

TableViewCell should not get reuse

I am using UITableview in my project, and also i have UITableviewcell for that UITableview. The problem is that cells are getting reused.if it get reused, when i select button i want the image to be changed for single cell and button isSelected =YES. So the button's selected image is getting reuse for every cell.
So,kindly help me out with this problem .
// In indexPathRow before return
cell button.tag=indexPath.row;
In buttonclick action
//
-(IBAction)BtnAction:(UIControl *)sender
{
UIButton *button = (UIButton*)sender;
NSIndexPath *buttonIP = [NSIndexPath indexPathForRow:sender.tag];
//Type cast it to CustomCell
CustomCell *cell = (CustomCell*)[table cellForRowAtIndexPath:buttonIP];
if(button.isSelected==NO)
{
cell.Imageview.image=selectedImage;
button.isSelected=YES;
}
else
{
cell.Imageview.image=OldImage;
button.isSelected=NO;
}
[table reloaddata];
}
insted of using the above code you can use this line of code
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(firstTime)
{
[tableView cellForRowAtIndexPath:indexPath].imageView.image = [UIImage imageNamed:#"selected image"];
[attatancearry addObject:value];
}
if(secondTime)
{
[tableView cellForRowAtIndexPath:indexPath].imageView.image = [UIImage imageNamed:#"absent image"];
[attatancearry removeObject:value];
}
}
and add the value to an another nsmutablearry as the attendence is added and when you press the same cell again you have to remove the value from the nsmutablearray you have added before, and reset the image back to absent. but you don't have to reload the entire tableview it will just change the cell which you have select right now. as your adding the value or index number of the cell into a array you can use the selection value for further use.
you have to see the cell which you have selected, and set a flag value as the selected index. and reload the table.
when you reload the table check the flag value and set the image which you want to set for the cell which is selected. it will give you different cell selection.
for example
int selectionFlag = -888;
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(selectionFlag == indexPath.row)
{
//set the image you want to set for the cell which is selected.
}
else
{
//the normal image you want to set.
}
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
selectionFlag = indexPath.row;
[tableView realoadData];
}

How can I reuse UITableViewCell only when needed?

I have a tableview that has a UISegmentedControl as the tableHeaderView for my tableViewController. I have two arrays as properties that will populate the cells based on which index is selected in the segmentedController. When the two arrays have a different number of items in them, I get an out of bounds exception, which I would expect. When the number of items is the same in each array, I can reload the tableView data and everything works fine. How can I prevent the controller from trying to reuse a cell when the cell isn't needed anymore?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Downloads Cell" forIndexPath:indexPath];
// 1st option selected
if (self.downloadTypeSelector.selectedSegmentIndex == 0) {
if (indexPath.row < self.talkDownloads.count) {
// Configure the cell...
Talk *talk = [self.talkDownloads objectAtIndex:indexPath.row];
cell.textLabel.text = talk.title;
cell.detailTextLabel.text = talk.speaker;
}
// 2nd option selected
} else if (self.downloadTypeSelector.selectedSegmentIndex == 1) {
if (indexPath.row < self.speechesDownloads.count) {
Speech *speech = [self.speechesDownloads objectAtIndex:indexPath.row];
cell.textLabel.text = speech.title;
cell.detailTextLabel.text = speech.speaker;
}
}
return cell;
}
I see that you have a condition that checks downloadTypeSelector.selectedSegmentIndex and uses the respective array accordingly, which is fine.
Do you have the same if else condition in - tableView:numberOfRowsInSection: that checks the selectedSegmentIndex and returns the correct count of the respective array?
If that works then I think checking if indexPath.row < array.count will be obsolete too.
Cheers!

Persistently keeping a UITableView selected cell's UIAccessoryType with NSUserDefaults

There are many questions on this topic and I've been trying this for hours with the various options, but I'm in need of some real assistance with this.
The main premise is: I have a Tab Bar controller with two tabs; the first is a Timeline and the second is the In-App Settings (both table view controllers). In the In-App settings, when I select "Keyboard", I'm taken to another Table view by segue with two static cells: Light and Dark.
What I want to achieve is: When I tap on one of these cells, it puts a checkmark accessory view on that cell.
Using NSUserDefaults, I want to maintain the selected cell and set that cells' accessoryType to checkmark on the next view reload.
I followed this questions' selected answer and while my code allows me to select either the Light or Dark cell; it never maintains that in between reloading.
Here's my cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
self.selectedKeyboardCell = [[NSUserDefaults standardUserDefaults] objectForKey:#"Selected"];
NSLog(#"The selected keyboard is %#", self.selectedKeyboardCell);
if([self.checkedIndexPath isEqual:indexPath])
{
NSLog(#"Loaded");
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
NSLog(#"Not Loaded");
cell.accessoryType = UITableViewCellAccessoryNone;
/*if ([self.selectedKeyboardCell isEqualToString:#"Light"])
{
NSLog(#"P");
if (indexPath.row == 0)
{
NSLog(#"Q");
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
cell.accessoryType = UITableViewCellAccessoryNone;
}
else
{
NSLog(#"R");
if (indexPath.row == 1)
{
NSLog(#"Z");
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
cell.accessoryType = UITableViewCellAccessoryNone;
*/
}
My didSelectRowAtIndexPath is:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Uncheck the previous checked row
if(self.checkedIndexPath)
{
UITableViewCell* uncheckCell = [tableView
cellForRowAtIndexPath:self.checkedIndexPath];
uncheckCell.accessoryType = UITableViewCellAccessoryNone;
}
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
self.checkedIndexPath = indexPath;
self.selectedKeyboardCell = cell.textLabel.text;
[[NSUserDefaults standardUserDefaults] setObject:self.selectedKeyboardCell forKey:#"Selected"];
self.selectedKeyboardTheme = cell.textLabel.text;
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[[NSUserDefaults standardUserDefaults] setObject:self.selectedKeyboardTheme forKey:#"Keyboard"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
I have a [self.keyboardTableView reloadData]; in my viewWillAppear.
I have commented out some code I've been trying, but no matter how many posts I read, I just cannot find the solution here.
This question talks about shifting code to the viewWillAppear but I have no idea what code he's saying: Ho to refresh view and tableview, and checkmark a default setting cell on load?
Problem: I want to select a cell in the Table View and have that cell selected with a checkmark accessory type the next time (and every time this view loads). It's a static table and the text is ONLY ever going to say Light or Dark.
In the cellForRowAtIndexPath, self.selectedKeyboardCell accurately tells me whether it's Light or Dark, depending on which cell was selected before the view was loaded. However, how do I say "If Light, set the checkmark on the FIRST cell and remove the checkmark from the second cell and if Dark, set the checkmark on the SECOND cell and remove the checkmark from the first cell".
Update:
For clarity, I am able to select either the Light or Dark static Table view cell and whichever cell I select, the checkmark appears correctly. The problem is when I go to another view and come back to this view, the previously selected cell does not show. I'm saving the selectedCell's textLabel to NSUserDefaults and in the NSLog, it shows me the text of the selected cell but it does not show me the checkmark on the selected cell.
Update Two:
In order to set the default selected cell to Light the first time the app was run, I did the following in the cellForRowAtIndexPath:
if (!self.selectedKeyboardCell)
{
self.checkedIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
Any guidance on this would be seriously appreciated.
Honestly your code is a little confusing and it would help, if you had said a little more what actually works. But:
In the viewWillAppear you should just set the self.checkedIndexPath:
if ([self.selectedKeyboardCell isEqualToString:#"Light"])
{
self.checkedIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
}else{
self.checkedIndexPath = [NSIndexPath indexPathForRow:1 inSection:0];
}
[self.keyboardTableView reloadData];
right?

How to show the pre-checked UITableViewCells at the time of editing the form.

I have one of the form in which the user is allowed to select some fields, For selected items i used [cell setAccessoryType:UITableViewCellAccessoryCheckmark]; ,The logic i have used is , I have taken the emptyMutableArray and at the time of didSelectRowAtIndex delegate method i checked if the selected indexPath is present in that emptyMutableArray, If its present means the item is already selected then i remove that indexPath from that emptyMutableArray, But if that indexPath is not present in it, i add the indexPath in that emptyMutableArray. Ok after that i reloads the same tableView and at that time in the method cellForRowAtIndexPath i again have one if statement for setting the cell AccessoryType where i checks if the indexPath is present in the emptyMutableArray if its there i set the AccessoryType of cell to UITableViewCellAccessoryCheckmark otherwise i set it to UITableViewCellAccessoryNone,
After that Where the problem coming is when the user will try to edit such a form then i want to show the already selected items and thats what the issue , You all will understand the problem from my code for that.
This is the cellForRowAtIndexPath delegate methods code which decides which cell should have check mark
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.selectionStyle=UITableViewCellSelectionStyleNone;
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.font=[UIFont fontWithName:#"verdana" size:13];
cell.backgroundColor=[UIColor clearColor];
else if (tableView==roleTableView) {
cell.textLabel.text = [roleAry objectAtIndex:indexPath.row];
if([emptyMutableArray containsObject:indexPath]){
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
} else {
[cell setAccessoryType:UITableViewCellAccessoryNone];
}
}
cell.textLabel.textColor = [UIColor darkGrayColor];
return cell;
}
and this is the didSelectRowAtIndexPath method of tableView from where the user decides which item should be selected or deselected.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
else if (tableView==roleTableView){
NSLog(#"IndexPath Obj:-%#",indexPath);
if([emptyMutableArray containsObject:indexPath]){
[emptyMutableArray removeObject:indexPath];
[roleMutableAry removeObject:[roleIdAry objectAtIndex:[roleAry indexOfObject:[NSString stringWithFormat:#"%#",[roleAry objectAtIndex:indexPath.row]]]]];
}
else {
[emptyMutableArray addObject:indexPath];
[roleMutableAry addObject:[roleIdAry objectAtIndex:[roleAry indexOfObject:[NSString stringWithFormat:#"%#",[roleAry objectAtIndex:indexPath.row]]]]];
}
NSLog(#"roleMutableAry:%#",roleMutableAry);
if ([roleMutableAry count]==0) {
//[roleBtn setTitle:#"Select role" forState:UIControlStateNormal];
}
[roleTableView reloadData];
[self loadInvitees];
[peoplesOfDiffRoleTableView reloadData];
}
}
I know it was typical for me to express the exact problem, But once you understand the code i will explain where the problem occurring while showing the already selected (checked) items second time.
You cannot depend upon the IndexPath for setting some properties of cell as because the cells get reused. What I understand from your question is that you want to keep a track of the cell that has been selected and later want the table view to accordingly show the accessory type. For this you should have an array which will contain objects that will have a property as "isSelected" or anything you want. This property will help you in setting you accessory type based on its value. In your cellForRowAtIndex you need to fetch the corresponding object out from the array and check for the property "isSelected" or whatever you define and accordingly set the accessory type of the cell. I hope this will help you. Cheers!

Can't Change Accessory Type From didSelectRowAtIndexPath?

Before I post the question itself, I need to state this is a jailbreak app. This is why I'm writing in "bizarre" folders in the filesystem.
Let's continue.
Here is my cellForRowAtIndexPath method:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"pluginCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
}
if(indexPath.row == 0)
{
cell.textLabel.text = #"default";
}else
{
//Get the plugin's display name.
NSBundle *currentPlugin = [[NSBundle alloc] initWithPath:[NSString stringWithFormat:#"/Library/Cydeswitch/plugins/%#", [plugins objectAtIndex:indexPath.row - 1], nil]];
cell.textLabel.text = [[currentPlugin localizedInfoDictionary] objectForKey:#"CFBundleDisplayName"];
if(cell.textLabel.text == nil)
{
//No localized bundle, so let's get the global display name...
cell.textLabel.text = [[currentPlugin infoDictionary] objectForKey:#"CFBundleDisplayName"];
}
[currentPlugin release];
}
if([[[cell textLabel] text] isEqualToString:[settings objectForKey:#"pluginToExecute"]])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
currentCell = [cell retain];
}
return cell;
}
Like you can see, this method uses a member called currentCell to point to the cell that is currently "selected". This is an options table and the user should be able to have only one cell with the Checkmark accessory icon at any time.
When the use selects another cell, he is changing an option and the Checkmark is supposed to disappear from the current cell and appear in the newly appeared cell. I do that like this:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
currentCell.accessoryType = UITableViewCellAccessoryNone;
[currentCell release];
currentCell = [[self tableView:tableView cellForRowAtIndexPath:indexPath] retain];
NSLog(#"CURRENT CELL %#", currentCell.textLabel.text);
currentCell.accessoryType = UITableViewCellAccessoryCheckmark;
}
But it doesn't work. The moment I tap another cell, the Checkmark correctly disappears from the old cell, but it never shows up in the new cell.
I know the selection work fine because that NSLog there prints the new cell's text just fine.
I have tried keeping track of the indexPath before, but it didn't work at all. When I tried using indexPaths instead of pointers to cells, when the user tapped the cell nothing happened at all (at least with my current approach the checkmark disappears from the old cell).
I think it has something to do with cellForRowAtIndexPath because if I keep pointing at the cells the checkmark disappears, but for some reason when trying to change the accessory type from a cell fetched with cellForRowAtIndexPath it doesn't seem to work at all.
Any help will be appreciated.
Typo? Try this:
currentCell = [[self.tableView cellForRowAtIndexPath:indexPath] retain];
You mustn't keep track of the last selected cell the way you are. Cell's get reused. Use an ivar to keep track of the indexPath or some other key appropriate to your data.
Then in the didSelect... method you get a reference to the old cell using the saved indexPath or key. In the cellForRow... method you need to set the proper accessoryType based on whether the current indexPath matches your saved indexPath.
Lastly, do not call your own delegate/data source method. When getting a reference to a cell, ask the table view for it directly.
BTW - you are over-retaining currentCell in your cellForRow... method. There is no need to retain it all in that method unless it is the first time you are making the assignment.

Resources