0

I'm going to implement a specific ViewController which - popups each time my app comes in foreground, - requires an access password.

Since the app is already mature and a bit complex, I'd rather handle this password screen as a separate ViewController that appears on top of other VCs, invoked from AppDelegate by methods such as applicationDidBecomeActive:

    if (self.pwdVc==nil)
        self.pwdVc = [mainStoryboard instantiateViewControllerWithIdentifier:@"passwordScreen"];
    [self.window.rootViewController presentViewController:self.pwdVc animated:NO completion:nil];

The issue with this approach seems to be: when the app comes in foreground, the password ViewController is not already drawn on screen, and for a short lapse the real app screen is shown in clear.

Even if I trigger presenting the password ViewController in applicationDidEnterBackground, in advance, the actual screen redraw will always happen when the apps comes back in foreground, thus showing the app screen unwantedly.

May anyone please suggest the most proper method to have this password screen hide the underlying ViewControllers with no delay?

Methods with alpha settings switching from 0.0 to 1.0 also seem taking some time to show, and did not work up to now.

Thank you!

frank
  • 69
  • 1
  • 7

3 Answers3

1

Use this method to present viewController on top of all.

+ (void)presentViewController:(UIViewController *)viewController animated:(BOOL)animated complete:(void (^)(void))callback
{
    UIViewController *activeModalVC = [[[[UIApplication sharedApplication] delegate] window].rootViewController presentedViewController];
    if (activeModalVC) {
        [activeModalVC presentViewController:viewController animated:animated completion:callback];
    } else {
        [[[[UIApplication sharedApplication] delegate] window].rootViewController presentViewController:viewController animated:animated completion:callback];
    }
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Making passwordView to load
    self.pwdVc = [[UIStoryboard storyboardWithName:@"Main" bundle:nil]
                  instantiateViewControllerWithIdentifier:@"passwordScreen"];
    [self.pwdVc view];
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    [AFAppDelegate presentViewController:self.pwdVc animated:NO complete:nil];
}
l0gg3r
  • 8,864
  • 3
  • 26
  • 46
  • Thank you @l0gg3r! I tried your suggestion, but unfortunately the Password ViewController still takes some time before appearing on top, as soon as the app gets back in foreground. Could be I'm missing something relevant? – frank Sep 23 '14 at 07:42
  • Try to place that call in applicationDidBecomeActive, and pass animated property NO. I think this should fix the issue. – l0gg3r Sep 23 '14 at 07:45
  • hm... does it show PasswordVC, but after some delay? – l0gg3r Sep 23 '14 at 08:02
  • Of course, it shows correctly, but after some delay. – frank Sep 23 '14 at 08:31
  • Still delaying appearance on top. I've taken a video of this effect [here](http://www.cisotta.com/share/pwdScreenDelayDemo.mov). Actually let me say I couldn't identify the `AFAppDelegate` class. I used my class invocation `[PasswordViewController presentViewController:self.pwdVc animated:NO complete:nil];` – frank Sep 23 '14 at 09:01
  • actually you see a snapshot image and some presentation delay. a dirty solution is, to subview some temporary image on window, and after presenting passwordVC remove that image. – l0gg3r Sep 23 '14 at 09:08
  • I'm afraid that any attempt to mask the view / add a subview would take effect at user eyes only after the app goes back to foreground, after the small time lapse during which the latest screen snapshot is shown. I'm wondering how the various secure containers apps succeed in immediately putting the lock screen at the topmost level, while the app comes in foreground... – frank Sep 23 '14 at 15:29
0

I found another thread on this topic, from 2013, saying one may execute instructions to remove relevant data from the current view in applicationDidEnterBackground before the screen snapshot is taken.

Actually it does not work for me: snapshot for returning to foregroung is taken with screen content before the execution of applicationDidEnterBackground or even applicationWillResignActive.

Community
  • 1
  • 1
frank
  • 69
  • 1
  • 7
0

The solution came from this topic, and is based on putting an opaque "screen lock" view on top of any other view and control it by its hidden property in the proper AppDelegate methods!

So:

  1. Add a view on top of any other view in Interface Builder, and initially set its view.hidden=NO in didFinishLaunchingWithOptions;
  2. Ensure to set view.hidden=NO in applicationWillResignActive, since this call will occur both when sending app to background and when invoking the multitasking screen;
  3. Place a button in the "screen lock" view, invoking a method which sets view.hidden=YES when password is correct.

Optionally use layer.zPosition to adjust the order of visible views.

Et voilà! Thank you all!

Community
  • 1
  • 1
frank
  • 69
  • 1
  • 7