This is how I am currently saving the state of the cell checkmark (which works):
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {\
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[defaults setBool:YES forKey:cell.textLabel.text];
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
[defaults setBool:NO forKey:cell.textLabel.text];
}
[defaults synchronize];
}
This is how I am attempting to retrieve them:
-(void)viewWillAppear:(BOOL)animated {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
for (int section = 0; section < [self.tableView numberOfSections]; section++) {
for (int row = 0; row < [self.tableView numberOfRowsInSection:section]; row++) {
NSIndexPath* cellPath = [NSIndexPath indexPathForRow:row inSection:section];
UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:cellPath];
if (![defaults valueForKey:cell.textLabel.text]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
}
}
The NSUser defaults are saving correctly (I know this because of NSLog), but when I exit and return to the table view, no cells have checkmarks.
If I change the line:
if (![defaults valueForKey:cell.textLabel.text]) {
to
if ([defaults valueForKey:cell.textLabel.text]) {
the table view reappears with all cells having checkmarks.
Thanks
Your problem is that [self.tableView cellForRowAtIndexPath:cellPath]; can return nil if there is currently no cell for that row in the table - which is almost certainly the case in viewDidLoad because the view isn't visible yet. Even if the table has loaded that method can still return nil if the cell has scrolled out of view and the cell object has been released or reused.
You would be better storing your selection state in an NSMutableDictionary and storing the dictionary in NSUserDefaults. You can then check the contents of the dictionary in cellForRowAtIndexPath and set the accessory accordingly.
Related
I am trying to implement the select alarm day page, like the alarm screen in iPhone. I can mark multiple cells at a time but the problem is that when I mark row 1 & 2 I can't identify that the two rows are marked. Can anyone help me?
here is the screen, that I have implemented ]
NSUserDefaults *dd=[NSUserDefaults standardUserDefaults];
UITableViewCell *cell= [tableView cellForRowAtIndexPath:path];
if (path.row==0) {
if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
} else {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[dd setObject:#"sun" forKey:#"key"];
day=#"sun";
}
}
if (path.row==1) {
if (Tablcell.accessoryType == UITableViewCellAccessoryCheckmark) {
Tablcell.accessoryType = UITableViewCellAccessoryNone;
} else {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
day=#"mon";
[dd setObject:#"mon" forKey:#"key"];
}
[dd synchronize];
}
Step: 1
Create a mutual array
NSMutableArray *selectedIndexArray;
Step : 2
In didSelectItemAtIndexPath Method add index value in selectedIndexArray
NSString *str = [NSString stringWithFormat:#"%ld",(long)indexPath.item];
[selectedIndexArray addObject:str];
Step : 3
In didDeselectItemAtIndexPath Method remove index value from selectedIndexArray
NSString *str = [NSString stringWithFormat:#"%ld",(long)indexPath.item];
if([selectedIndexArray containsObject:str]){
[selectedIndexArray removeObject:str];
}
Step : 4
Now you can use this array to show / hide checked button in cell
Following a previous SO question, I was trying to get my tableView cell selection right, with saving and other stuff.
I've making it toggle, but can't save the indexPath and retrieve it to display the selection even when the user leaves the view or restarts the app.
Here's the code:
#property (nonatomic, strong) NSIndexPath* lastIndexPath;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
if([self.lastIndexPath isEqual:indexPath])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
...
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if(self.lastIndexPath)
{
UITableViewCell* uncheckCell = [tableView
cellForRowAtIndexPath:self.lastIndexPath];
uncheckCell.accessoryType = UITableViewCellAccessoryNone;
}
if([self.lastIndexPath isEqual:indexPath])
{
self.lastIndexPath = nil;
}
else
{
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
self.lastIndexPath = indexPath;
}
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[NSNumber numberWithInt:self.lastIndexPath.row] forKey:#"selectedCell"];
[defaults synchronize];
}
How can I save the indexPath and retrieve it to display the selection even after the user closes and reopens the view?
Thanks in advance.
You should add this in viewWillAppear.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSNumber *lastRow = [defaults objectForKey:#"selectedCell"];
if (lastRow) {
self.lastIndexPath = [NSIndexPath indexPathForRow:lastRow.integerValue inSection:0];
}
When you restart the app, you should get the lastIndexPath from NSUserDefaults, otherwise it is nil.
You should do two things:
1) In didSelectRowAtIndexPath, reload the previously selected row if it is visible
2) In cellForRowAtIndexPath, do a check in there if indexpath.row is the same as your selected cell. If it is, give it the check mark, if not then don't
I am trying to save checked and remove unchecked tablecells into NSUserdefaults but it seems to ignore it as the NSLog in the for loop never gets called not sure why, is there something wrong with this code? (Everything shows up right and the cells get checked and un-checked correctly)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//setting up nsUser
userDefaults = [NSUserDefaults standardUserDefaults];
//current row text
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSString *cellName = cell.textLabel.text;
// Check if current row is selected
Boolean isNowChecked = NO;
if([tableView cellForRowAtIndexPath:indexPath].accessoryType == UITableViewCellAccessoryCheckmark)
{
isNowChecked = YES;
}
if(isNowChecked)
{
NSLog(#"UNCHECKING");
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone;
//takes away highlight from selection
[tableView deselectRowAtIndexPath:indexPath animated:YES];
//retrieve from nsdefaults
NSMutableArray *arrayOfCategories = [userDefaults objectForKey:#"categories"];
NSMutableArray *categoriesSelected;
//remove from nsuserdefaults
//add to nsuserdefaults
for (NSString* o in arrayOfCategories)
{
NSLog(#"%#",o);
if([o isEqualToString:cellName]) {
} else {
[categoriesSelected addObject:o];
}
}
//set for nsdefaults
[userDefaults setObject:categoriesSelected forKey:#"categories"];
[userDefaults synchronize];
}
else
{
NSLog(#"CHECKING");
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
//takes away highlight from selection
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSMutableArray *categoriesSelected;
//add to array
[categoriesSelected addObject:cellName];
//retrieve from nsdefaults
NSMutableArray *arrayOfCategories = [userDefaults objectForKey:#"categories"];
//add to nsuserdefaults
for (NSString* o in arrayOfCategories)
{
NSLog(#"%#",o);
[categoriesSelected addObject:o];
}
//set for nsdefaults
[userDefaults setObject:categoriesSelected forKey:#"categories"];
[userDefaults synchronize];
}
}
Change this line, otherwise you have an uninitialized Array:
NSMutableArray *categoriesSelected;
to:
NSMutableArray *categoriesSelected = [[NSMutableArray alloc] init];
Update: After looking at your code there are actually quite a few things that should be improved.
You should load the list of checked categories once in viewDidLoad and keep the list in an instance variable.
It's not a good idea to check to see if a cell is currently checked or not by looking at the cell's accessory type. Your data model should tell you which rows are checked.
You have a lot of duplicate code to toggle the checked state of a cell.
Use a set, not an array, to keep track of the checked cells.
Try the following changes:
Add an NSMutableSet instance variable to your class:
NSMutableSet *_checkedCategories;
In viewDidLoad, load the set:
NSArray *checkedCategories = [[NSUserDefault standardUserDefaults] objectForKey:#"categories"];
if (checkedCategories) {
_checkedCategories = [[NSMutableSet alloc] initWithArray:checkedCategories];
} else {
_checkedCategories = [[NSMutableSet alloc] init];
}
Now update your didSelectRow method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellName = ...; // get the cell name from your data model, not the cell
BOOL isChecked = [_checkedCategories containsObject:cellName];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (isChecked) {
[_checkedCategories removeObject:cellName]; // no longer checked
cell.accessoryType = UITableViewCellAccessoryNone;
} else {
[_checkedCategories addObject:cellName]; // now checked
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[[NSUserDefaults standardUserDefaults] setObject:[_checkedCategories allObjects] forKey:#"categories"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
Here is the Video showing problem in action:
http://www.youtube.com/watch?v=vWznBUsppRg
I'm getting weird behavior with my Array when adding a cell to a section. I'm developing a mobilesubstrate tweak..this is why I didn't post entire methods.
The Cell is getting added to the bottom of the section when turned ON. But when turned off, the cell under the UISwitch cell gets removed like intended but screws up indexPaths.
top of .m:
static NSUserDefaults *prefs = nil;
static UISwitch *switchView = nil;
static NSMutableArray *array = nil;
-viewDidLoad I'm using -
BOOL state = [prefs boolForKey:#"betaVid"];
array = [[NSMutableArray alloc] initWithObjects:#"Show Cached Videos", #"(Beta) Send Videos", #"Video Quality", #"Visit Website", #"Made by: #CokePokes", nil];
if (state == FALSE){
[array removeObject:#"Video Quality"]; //remove from array
}
-numberOfRowsInSection i have:
if (section == 3){
return [array count];
}
return section;
-cellForRowAtIndexPath i have:
prefs = [NSUserDefaults standardUserDefaults];
BOOL state = [prefs boolForKey:#"betaVid"];
if (indexPath.section == 3){
static NSString *CellIdentifier = #"Cell";
//NSString *CellIdentifier = [NSString stringWithFormat:#"Cell_%d",indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
cell.textLabel.text = [NSString stringWithFormat:#"%#", [array objectAtIndex:indexPath.row]];
NSInteger counted = [array count];
NSLog(#"~~~~~~~~~~~~~~~~~~~hmmmmm.... Really is: %d", counted);
if (counted == 4){
NSLog(#"~~~~~~~~~~~~~~~~~~~Array count is _4_. Really is: %d", counted);
if (indexPath.row == 0){
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.textAlignment = UITextAlignmentLeft;
} else if (indexPath.row == 1){
switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
cell.accessoryView = switchView;
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
switchView.on = [prefs boolForKey:#"betaVid"];
[switchView addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
} else if (indexPath.row == 2){
//set up cell appearance..not important now
} else if (indexPath.row == 3){
//set up cell appearance..not important now
}
} else if (counted == 5){
NSLog(#"~~~~~~~~~~~~~~~~~~~~Array count is _5_. Really is: %d", counted);
if (indexPath.row == 0){
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.textAlignment = UITextAlignmentLeft;
} else if (indexPath.row == 1){
switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
cell.accessoryView = switchView;
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
switchView.on = [prefs boolForKey:#"betaVid"];
[switchView addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
} else if (indexPath.row == 2){
//set up cell appearance..not important now
}
return cell;
}
return indexPath;
And Finally The UISwitch switchChanged:
switchView = sender;
NSLog( #"The switch is %#", switchView.on ? #"ON" : #"OFF" );
prefs = [NSUserDefaults standardUserDefaults];
[prefs setBool:switchView.on forKey:#"betaVid"];
[prefs synchronize];
BOOL state = [prefs boolForKey:#"betaVid"];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:2 inSection:3];
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
if (state == TRUE) {
[self.table beginUpdates];
[array addObjectsFromArray:indexPaths];
[self.table insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
[self.table endUpdates];
[self.table reloadData];
} else if (state == FALSE){
[self.table beginUpdates];
[array removeObjectAtIndex:indexPath.row];
[self.table deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
[self.table endUpdates];
[self.table reloadData];
}
I've looked at almost every post concerning deleteRowsAtIndexPaths & insertRowsAtIndexPaths on Stack but none have a good amount of examples to work on/around.
Your data model (array) is getting corrupted by the following line when the switch is turned on:
[array addObjectsFromArray:indexPaths];
I think what you meant is:
[array insertObject:#"Video Quality" atIndex:indexPath.row];
If that's not it, it would help if you could explicitly state what the intended behavior is.
I'm new to ios development, and I'm using checkmark for cells in a UITableView.
I want storing checked cell in a NSUserDefaults database, When I reload the app, the checked cell that previously selected will be display, I'm try to defferent ways, but still failed to implement it.
Anybody can help me? I would be highly appreciate it. Sorry, my english is not good.
My code is below:
#import "FirstRowSubview.h"
#interface FirstRowSubview ()
#end
#implementation FirstRowSubview
#synthesize array = _array;
#synthesize lastIndexPath = _lastIndexPath;
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *list = [[NSMutableArray alloc] initWithObjects:#"ffgfgh", #"564654", #"56548", #"fgmjfgmf", #"ggkdj", nil];
self.array = list;
[list release];
}
- (void)viewDidUnload
{
[super viewDidUnload];
self.array = nil;
}
#pragma mark -
#pragma mark - TableView Datasource Methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_array count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *subviewCells = #"Cells";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:subviewCells];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:subviewCells];
}
NSUInteger oldRow = [_lastIndexPath row];
cell.accessoryType = (indexPath.row == oldRow && _lastIndexPath != nil) ?
UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
cell.textLabel.text = [_array objectAtIndex:indexPath.row];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[NSNumber numberWithInt:_lastIndexPath.row] forKey:#"lastIndexPath"];
[defaults synchronize];
return cell;
}
#pragma mark -
#pragma mark - TableView Delegate Methods
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
int newRow = [indexPath row];
int oldRow = (_lastIndexPath != nil) ? [_lastIndexPath row] : -1;
if (newRow != oldRow) {
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
newCell.accessoryType = UITableViewCellAccessoryCheckmark;
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:_lastIndexPath];
oldCell.accessoryType = UITableViewCellAccessoryNone;
[indexPath retain];
[_lastIndexPath release];
_lastIndexPath = indexPath;
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
}
It can be easy to store and retrieve checked cell(s) information with NSUserDefaults.
NSUserDefaults can store the data interms of Key / Value pair. Here in your case value could be boolean and key could combination of NSString & indexpath.row from UITableView.
You can make a functions like,
- (NSString *)getKeyForIndex:(int)index
{
return [NSString stringWithFormat:#"KEY%d",index];
}
- (BOOL) getCheckedForIndex:(int)index
{
if([[[NSUserDefaults standardUserDefaults] valueForKey:[self getKeyForIndex:index]] boolValue]==YES)
{
return YES;
}
else
{
return NO;
}
}
- (void) checkedCellAtIndex:(int)index
{
BOOL boolChecked = [self getCheckedForIndex:index];
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:!boolChecked] forKey:[self getKeyForIndex:index]];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_array count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Here, you can check for previously checked cells like
static NSString *subviewCells = #"Cells";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:subviewCells];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:subviewCells];
}
cell.textLabel.text = [_array objectAtIndex:indexPath.row];
if([self getCheckedForIndex:indexPath.row]==YES)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
//Use checkedCellAtIndex for check or uncheck cell
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[self checkedCellAtIndex:indexPath.row];
if([self getCheckedForIndex:indexPath.row]==YES)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
P.S. Please, note that, this method will only useful and suggested if order of data from your NSArray will always in same order. Other then NSUserDefaults you've options like SQLite Database or plist file or any other option! Thanks :)
This is a basic example of how to create an array of NSIndexPaths based on the current selection of the table and then saving that array into NSUserDefaults.
-(void)viewDidLoad
{
[super viewDidLoad];
for (NSIndexPath *indexPath in [[NSUserDefaults standardUserDefaults] mutableArrayValueForKey:#"mySavedMutableArray"]) {
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}
Here you can add objects to the array as they are selected
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[[[NSUserDefaults standardUserDefaults] mutableArrayValueForKey:#"mySavedMutableArray"] addObject:indexPath];
[[NSUserDefaults standardUserDefaults] synchronize];
}
Here you'll remove the deselected object from the array.
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
[[[NSUserDefaults standardUserDefaults] mutableArrayValueForKey:#"mySavedMutableArray"] removeObject:indexPath];
[[NSUserDefaults standardUserDefaults] synchronize];
}
The simplest way to get your code working is just put one line in view did load method as follow:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if([defaults valueForKey:#"lastIndexPath"])
_lastIndexPath = [defaults valueForKey:#"lastIndexPath"];
This will solve your problem :)
Happy Coding :)