Search code examples
iphonecore-datapersistenceuitableviewnsindexpath

How to restore the user-location in a drill down UITableViewController structure?


I have a CoreData model that contains Levels which can again contain childLevels.

I represent each level with a UITableViewController listing all childLevels. When a user taps a row a new UITableViewController is pushed onto the navigationController. No problem here.

How would I go about storing the user location within this table structure. Is there a best practice for doing this? I have no problem doing this if the depth of the structure was known, but somehow puzzled how to approach this with a undefined depth.

Should I store the NSIndexPath tapped by the user into an array and write it to disk?


Solution

  • Instead of using the NSIndexPaths tapped by the user I went with the underlying NSManagedObjects which is a lot safer (in case number or sorting of objects change) and faster (because I do not need the whole fetchRequest and or view).

    I subclassed the UINavigationController and did the following.

    When pushing a new TableViewController for a level (stored in parentLevel) I append this to an array in UserDefaults:

    - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
       [super pushViewController:viewController animated:animated];
    
       if([viewController isKindOfClass:[LevelTableViewController class]]){
           NSMutableArray *array = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:LevelTablesPersistentKey]];
           NSManagedObject *obj = [(LevelTableViewController*)viewController parentLevel];
    
           if(obj!=nil){
             [array addObject:[[obj objectID].URIRepresentation absoluteString]];
           } 
    
           [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithArray:array] objectForKey:LevelTablesPersistentKey];
    
       }
    }
    

    When I pop a viewController I simply remove the last entry from that array:

    - (UIViewController *) popViewControllerAnimated:(BOOL)animated{
      UIViewController *vc = [super popViewControllerAnimated:animated];
      // remove last object
      if([vc isKindOfClass:[LevelTableViewController class]]){
         NSMutableArray *array = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:LevelTablesPersistentKey]];
         [array removeLastObject];
         [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithArray:array] objectForKey:LevelTablesPersistentKey];
      }
    
      return vc;
    }
    

    I can then use this array when initializing the NavigationController when the app is next started to rebuild the tree:

    - (LevelNavigationController*) initWithRootViewController:(LevelTableViewController*)vc {
    if(self = [super initWithRootViewController:vc]){
        // Recreate structure from UserDefaults
        NSArray *array = [NSArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:LevelTablesPersistentKey]];
        [[NSUserDefaults standardUserDefaults] setObject:nil forKey:LevelTablesPersistentKey]; // set the array to nil -> will be rebuild when pushing viewcontrollers onto navigation stack
    
        NSPersistentStoreCoordinator *persistentStoreCoordinator = ...; // pointer to coordinator 
                NSManagedObjectContext * managedObjectContext = ...; // pointer to your context
        for (NSString *objId in array) {
            NSManagedObjectID *mobjId=[persistentStoreCoordinator managedObjectIDForURIRepresentation:[NSURL URLWithString:objId]];
            if(mobjId!=nil){
    
                NSManagedObject *obj = nil;
                NSError **err = nil;
                obj = [managedObjectContext objectWithID:mobjId];
    
                if(err==nil && obj){
                    if([obj.entity.name isEqualToString:@"Level"]){
                        // push level
    
                        LevelTableViewController *nextLevel = [[LevelTableViewController alloc] initWithStyle:UITableViewStylePlain];
                        nextLevel.parentLevel = (Level*)obj;
                        [self pushViewController:nextLevel animated:NO];
                        [nextLevel release];
                    } 
                } 
            }
        }
    
    }
    
    return self;
    
    }