0

i have a question regarding NSUserDefaults. I am trying to save the checkmark that i place on a cell and then retrieve it when the app crashes or when user closes app and so on. I tried to use this post post as a guide, but no luck, but here's what i have so far. The code from the post works, however, i only need one checkmark to be saved rather than many. How would i achieve this?

@implementation ClientsViewController

@synthesize clientsInt;        // This is just a variable that helps me do the drill down part of the rootviewcontroller
@synthesize checkedIndexPath;

- (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];
}

- (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];

    if([self getCheckedForIndex:indexPath.row]==YES)
    {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    }
    else
    {
        cell.accessoryType = UITableViewCellAccessoryNone;
    }
}

// Configure the cell...
if (clientsInt == 0) {
    cell.textLabel.text = [array1 objectAtIndex:indexPath.row];
}
else if (clientsInt == 1) {
    cell.textLabel.text = [array2 objectAtIndex:indexPath.row];
}
else if (clientsInt == 2) {
    cell.textLabel.text = [array3 objectAtIndex:indexPath.row];
}
else if (clientsInt == 3) {
    cell.textLabel.text = [array4 objectAtIndex:indexPath.row];
}

if([self.checkedIndexPath isEqual:indexPath])
{
    cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
    cell.accessoryType = UITableViewCellAccessoryNone;
}

return cell;
}

- (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 checkedCellAtIndex:indexPath.row];

if([self getCheckedForIndex:indexPath.row]==YES)
{
    cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
    cell.accessoryType = UITableViewCellAccessoryNone;
}

[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
Community
  • 1
  • 1
  • Are you properly setting up your defaults? They don't just magically appear, you know. – CodaFi Nov 29 '12 at 02:05
  • I believe so, i do it in `- (void) checkedCellAtIndex:(int)index` – Sero Eskandaryan Nov 29 '12 at 02:12
  • That's not what I mean. You need to initialize NSUserDefaults with actual defaults. Here's my favorite implementation: http://stackoverflow.com/questions/1546005/iphone-and-nsuserdefaults – CodaFi Nov 29 '12 at 02:13
  • what im trying to achieve is that when the user presses a cell, that cell is check marked and becomes their default, until they press another cell. – Sero Eskandaryan Nov 29 '12 at 02:13
  • yes, I understand exactly what you're trying to do, the problem lies in the way you're doing it. Wouldn't it be remarkably easier to query the cells for their state once at close, store it in an array, then dearchive it and set their state again, instead of querying defaults for every cell. – CodaFi Nov 29 '12 at 02:15
  • i was thinking of doing that also, but since my table view is not that comprehensive i thought it wouldnt make a difference. So say if i were to do that, would i put the indexPath.row in a NSMutableArray? – Sero Eskandaryan Nov 29 '12 at 02:19

3 Answers3

1

Maybe try something like this every time the user checks a row:

[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:index] forKey:@"kCheckedBoxKey"];

Since each time, you would save the index under the same key (@"kCheckedBoxKey"), only one index will ever be stored, and it will always be the latest one that the user checked. All you would need to do the next time you load is ask userDefaults if it can find a value for the key @"kCheckedBoxKey", and if so, you would respond by programatically checking the index that was stored.

(you'd of course also want to clean it up by using #define CHECKED_KEY @"kCheckedBoxKey" at the top of the file, and use CHECKED_KEY instead of the literal string to protect against misspellings. At any rate, the point is to make sure you always save & restore using that same key.)

jankins
  • 670
  • 5
  • 16
  • i tried to put that in `didSelectRowAtIndex:` and set index to indexPath.row (since there are no other sections) and i put ` if ([[NSUserDefaults standardUserDefaults] integerForKey:@"kCheckedBoxKey"]) { cell.accessoryType = UITableViewCellAccessoryCheckmark; NSLog(@"Check Retrieved"); }` in my `cellForRowAtIndexPath`. But still nothing. It actually checks every cell in the view. Maybe im doing something wrong in the above if statement? – Sero Eskandaryan Nov 29 '12 at 02:36
  • What you'd want to do is first make a `@property (assign, nonatomic) int restoredIndex;`. and `@property (assign, non atomic) BOOL didRestoreIndex;`. Then put this in viewDidLoad: `NSValue *checkedRow = [[NSUserDefaults standardUserDefaults] valueForKey:@"kCheckedBoxKey"];` `if (checkedRow) { _restoredIndex = [checkedRow intValue]; _didRestoreIndex = YES; }` Now in cellForRowAtIndexPath, check: `if (if _didRestoreIndex && indexPath.row == restoredIndex) – jankins Nov 29 '12 at 02:46
  • * some typos in previous comment - I can't edit because it's been 5 minutes since I posted. The final line should be: Now in cellForRowAtIndexPath, check: `if (_didRestoreIndex && indexPath.row == restoredIndex) { cell.accessoryType = UITableViewCellAccessoryCheckmark; NSLog(@"Check Retrieved"); }` (note: you need _didRestoreIndex because otherwise when you compare _restoredIndex to index 0, it will return YES, and always check the first row.) – jankins Nov 29 '12 at 02:53
  • This works and the checkmarks do get retrieved :) one small bug is that when the app opens and it has the retrieved check marks, but when i press another cell to change the default, the retireved cell still has the checkmark, i have to click on it twice and then select another cell for it to get removed and then im back at one check mark in the list. any ideas why its doing that? i think its from `didSelectRowAtIndex:` part – Sero Eskandaryan Nov 29 '12 at 03:01
  • hmm...I think the problem is that just changing the accessory does not actually select the row. Try programatically selecting the index path instead of setting cell.accessoryType. try modifying the part in viewDidLoad, `if (checkedRow) { _restoredIndex = [checkedRow intValue]; NSIndexPath *indexPath = [[NSIndexPath alloc] indexPathForRow:_restoredIndex inSection:0]; [self tableView:_tableView didSelectRowAtIndexPath:indexPath]; }` This way you could get rid of the BOOL _didRestoreIndex, so it's cleaner because you won't be checking the saved index each time cellForRowAtIndexPath is called. – jankins Nov 29 '12 at 03:26
  • if that doesn't work, then there must be another similar way to do it there in viewDidLoad. Resarch how to programatically select or check a row in a UITableView. – jankins Nov 29 '12 at 03:27
  • i almost forgot to mention...in the second comment, NSValue has to be NSNumber i believe. Because if i use NSValue, i get an error saying intValue `No visible @interface for 'NSValue' declares the selector 'intValue'` and actually this code is not saving the checkmark anymore. And the issue mentioned above is still happening – Sero Eskandaryan Nov 29 '12 at 03:30
1

I recently had to save the state of each cell in my table view when the user selected or deselected them to add or remove checkmarks. Here is the snippet of code I used to save to a .plist file (let me know if you need the whole implementation I came up with:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *contentForThisRow = [[self list] objectAtIndex:[indexPath row]];
    NSString *CellIdentifier = [NSString stringWithFormat:@"Cell%d%d", indexPath.section, indexPath.row];
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if(cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    NSString *documentDirectory = [(AppDelegate *)[[UIApplication sharedApplication] delegate] applicationDocumentsDirectory];
    NSString *PlistPath = [documentDirectory stringByAppendingPathComponent:@"Settings.plist"];
    NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:PlistPath];

    NSString *row = [NSString stringWithFormat:@"%d",indexPath.row];

    if([[dict objectForKey:row]isEqualToString:@"0"])
    {
        cell.accessoryType = UITableViewCellAccessoryNone;
    }
    else
    {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    }

    [[cell textLabel] setText:contentForThisRow];

    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *documentDirectory = [(AppDelegate *)[[UIApplication sharedApplication] delegate] applicationDocumentsDirectory];
    NSString *PlistPath = [documentDirectory stringByAppendingPathComponent:@"Settings.plist"];

    NSMutableDictionary *plist = [NSMutableDictionary dictionaryWithContentsOfFile:PlistPath];

    UITableViewCell *cell = [self._tableView cellForRowAtIndexPath:indexPath];

    NSString *row = [NSString stringWithFormat:@"%d",indexPath.row];

    if(cell.accessoryType == UITableViewCellAccessoryNone)
    {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
        NSString *on = @"1";
        [plist setObject:on forKey:row];
        [plist writeToFile:PlistPath atomically:YES];
    }
    else if(cell.accessoryType == UITableViewCellAccessoryCheckmark)
    {
        cell.accessoryType = UITableViewCellAccessoryNone;
        NSString *off = @"0";
        [plist setObject:off forKey:row];
        [plist writeToFile:PlistPath atomically:YES];
    }
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}
klcjr89
  • 5,862
  • 10
  • 58
  • 91
  • I might consider this also as an option. But how did you retrieve it from the plist? I have zero experience with plist lol – Sero Eskandaryan Nov 29 '12 at 02:46
  • Im getting an error on this line `NSString *documentDirectory = [(AppDelegate *)[[UIApplication sharedApplication] delegate] applicationDocumentsDirectory];`it says expecting ] right before delegate. What do i have to replace delegate with? – Sero Eskandaryan Nov 29 '12 at 05:05
  • Change that line to this instead: `NSString *paths = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];` – klcjr89 Nov 29 '12 at 05:14
  • it doesnt seem to be working properly. It selects all the rows and it doesnt reload the checkmarks – Sero Eskandaryan Nov 29 '12 at 05:25
  • Without seeing your code, it will be hard to tell what's causing the issue. The code I posted above is working fine for my project. – klcjr89 Nov 29 '12 at 05:28
  • do i have to manually create the Settings.plist file? I dont see it in my app directory. – Sero Eskandaryan Nov 29 '12 at 05:28
  • Actually i didn't lol i fought with it all night but no luck. The answer below actually did the trick for me :) But i will work on this more and do some research on plists and hopefully i can do it this way too just in case i decide to change my mind :) – Sero Eskandaryan Nov 30 '12 at 01:58
0

You only need to save the index of the selected row

Hosam
  • 101
  • 2
  • 7