views:

432

answers:

2

Before iOS4, my app's initial view controller would check a passcode on/off settings variable in viewWillAppear and if set on, present a modal passcode screen that would stay there until the correct passcode was entered or the Home button was pressed.

With iOS4, if my app has been in the background, I would like the user to feel comfortable that the data contained within the app is not easily accessible if they were to hand their phone to someone to use.

Since the app could return to any screen (the screen that the app was last on), I figured I would use the UIApplicationWillEnterForegroundNotification all over the place with local selectors (duplicate enterPasscode methods), each having the correct view controller to push based on the local screen, but there has to be a better way.

I may be having a slight conceptual misunderstanding here. Can someone suggest another approach or nudge me along to the next step. Can I have this as a shared method but still know the correct local view controller to push?

Thanks

EDIT/UPDATE:

Short version: It works, but may there still may be a better way (any help appreciated)...

I created a standard singleton with a view controller.

PasscodeView.h containing:

UIViewController *viewController;
@property (nonatomic, retain) UIViewController *viewController;

PasscodeView.m containing:

@synthesize viewController;

I put this in the AppDelegate:

-(void)applicationWillEnterForeground:(UIApplication*)application {

    PasscodeView *passcodeView = [PasscodeView sharedDataManager];
    UIViewController *currentController =  [passcodeView viewController];
    NSLog(@"%@", currentController);  //testing
    EnterPasscode *passcodeInput = [[EnterPasscode alloc] initWithNibName:@"Passcode" bundle:nil];
    [currentController presentModalViewController:passcodeInput animated:NO];
    [passcodeInput release];
}

and the following in all my viewDidLoad, updating the current view controller as I went into each screen (only 2 lines but still seems that there's a better way):

PasscodeView *passcodeView = [PasscodeView sharedSingleton];
passcodeView.viewController = [[self navigationController] visibleViewController];

I wish there were a way to have gotten the current view controller from applicationWillEnterForeground but I couldn't find it - any help here still appreciated.

For consistency, I changed a line and added a line to get a nav bar to match the rest of the app and to include a title.

UINavigationController *passcodeNavigationController = [[UINavigationController alloc] initWithRootViewController:passcodeInput];
[currentController presentModalViewController: passcodeNavigationController animated:NO];
A: 

You can centralize this behavior in your app delegate by implementing

-(void)applicationWillEnterForeground:(UIApplication*)application;

You might implement a singleton that stores the currently appropriate modal view controller, updating it in viewWillAppear in each of your view controllers.

Edit: I was assuming that you already had a series of view controllers that you wanted to show. I doubt you actually need it. If you have one called, say PasscodeInputController, then your applicationWillEnterForeground would look something like:

-(void)applicationWillEnterForeground:(UIApplication*)application {
    UIViewController *currentController =  [window rootViewController];
    PasscodeInputController *passcodeInput = [[PasscodeInputController alloc] init];

    [currentController presentModalViewController:passcodeInput animated:NO];
    [passcodeInput release];
}

I hope that addresses your question more directly.

Seamus Campbell
This is where I started but didn't know what to put for a view controller - I'll give this some thought and then a try.
Matt Winters
see edits above.
Seamus Campbell
I understand conceptually what you are trying to - present a passcode screen here rather than at each view controller - looks like it should work but it's not. Copied code exactly as is (changed PasscodeInputController to EnterPasscode), builds fine, no errors, no warning, but nothing comes up. I wasn't initially familiar with the rootViewController property of UIWindow but after reading what it is, it looks like it should work - although couldn't find any other examples. I tried a couple other things - tried using a navcontroller in there, tried a subview, makeKeyAndVisible but nothing.
Matt Winters
You're getting a non-nil value, though?
Seamus Campbell
Is your app nav controller based? Do you store the nav controller as a property in your app delegate? Try getting the current view (in [yourAppDelegate applicationWillEnterForeground]) with currentController = [[self navController] visibleViewController] and then call presentModalViewController on that.
Seamus Campbell
@Seamus See EDIT/UPDATE above. Thanks for getting me in the right direction. Your questions: No, currentController had been nil up until what I did as my 'solution'. Should probably have provided more details about the structure of the app. The app has a tab bar controller, with each of the 5 tabs having their own nav controller, lots of pushViewControllers and presentModelViewControllers and many different places to enter the background. Did not store nav controller as a property of the app delegate. I tried a variation but no help. Additional help appreciated to make your approach work.
Matt Winters
A: 

Your application's behaviour when reentering the foreground should be as similar as possible to when it launches for the first time. With this in mind, you could combine your applicationDidFinishLaunching: with applicationWillEnterForeground: but considering some views might already be loaded. The code is something like this:

id myState = (isReentering) ? [self validateState] : [self loadStateFromSave];
NSArray * keyForObjectToLoad = [self objectsToLoadForState:myState];
for(NSString * key in keyForObjectToLoad)
    if(![self objectForKey:key]) [self loadObjectForKey:key];

Depending on your app's detail it might require initial work, but it has some benefits:

  • It will ensure launching and relaunching are similar, hence the user experience is not "random".
  • You can free many unwanted memory easier when in the background.
  • It's easier to manage your application's state from a central place.
Mo