4

When my app goes to the background, it has to blur the current screen for data protection reasons. The content of the screen must not be seen in the task manager window, so the blurring routine has to be done before the app quits. As every UI update, the blurring must take place on the main loop. It simply adds a blurred view over the normal main view. However, this takes a little time to finish, and that's the problem.

I call my blurring routine from applicationWillResignActive: as Apple recommends. Now let's say it takes 0.2 seconds to finish the task. If the app will be reactivated within this short period of time, applicationDidBecomeActive: isn't called (which should unblur my screen again, so it's left blurred).

Maybe you think that it's not normal user behavior to "close" and "open" an app in such a short period of time, but think about tapping the upper edge of the screen and moving down a bit, just by accident while trying to tap a button that's at the upper edge. This will activate the Notification Center for just some fractions of a second. That's enough.

Just for making this problem transparent, have a look at this AppDelegate that will post the unbalanced calls in the console:


    @implementation UHAppDelegate

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        return YES;
    }

    - (void)applicationWillResignActive:(UIApplication *)application
    {
        NSLog(@"applicationWillResignActive - performing some tasks..."); 

        // let the main loop do some work...
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.2]];
    }

    - (void)applicationDidEnterBackground:(UIApplication *)application
    {
    }

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

    - (void)applicationDidBecomeActive:(UIApplication *)application
    {
        NSLog(@"applicationDidBecomeActive");
    }

    - (void)applicationWillTerminate:(UIApplication *)application
    {
    }

    @end

Any ideas how to achieve a balanced number of calls to applicationWillResignActive: and applicationDidBecomeActive: in this case...?

Thx a lot for your help Udo

  • Your applicationWillResignActive as written above does nothing ? – GuybrushThreepwood Jan 27 '14 at 17:01
  • It does - it posts a note in the console and blocks the main loop for 0.2 seconds, just as other method calls on the main loop would do. – Udo Hilwerling Jan 27 '14 at 17:03
  • If you perform the "dragging down" of the Notification Center at a normal speed and let it flip up again, you'll get a balanced call log ("applicationWillResignActive - performing some tasks", and on "flip up": "applicationDidBecomeActive"). However, if you just do a fast "tap & flip down / flip up" on the top of the screen, only "applicationWillResignActive - performing some tasks" will appear, although the app has gone active again) – Udo Hilwerling Jan 27 '14 at 17:09
  • [This chart](http://stackoverflow.com/a/17498665/653513) should help. But if what you say is true it might need some updating. – Rok Jarc Jan 27 '14 at 17:13
  • Yes, it seems the iOS drops the method call if the app has still not returned from applicationWillEnterBackground. – Udo Hilwerling Jan 27 '14 at 17:13
  • @rokjarc, applicationDidEnterBackground: does not help because (in my case) the updating of the UI will not take place. All work has to be done in applicationWillResignActive: – Udo Hilwerling Jan 27 '14 at 17:15
  • Are you testing on the simulator? [this question](http://stackoverflow.com/questions/19792850/display-a-view-or-splash-screen-before-applicationdidenterbackground-to-avoid-a) suggests you can update UI in applicationDidEnterBackground but it only works on real device? – Simon Jenkins Jan 27 '14 at 18:11
  • Ok I'm missing something - where's the code that blurs the ui? – GuybrushThreepwood Jan 27 '14 at 19:10
  • @Ohnomycoco That code should not be important. It just takes a snapshot of the UI as a UIImage, blurrs it and puts that blurred image above the visible UI by calling addSubView: . After that, I block the run loop in the same way as in the code above, to be sure that all UI updates have taken place before applicationWillResignActive: finishes. – Udo Hilwerling Jan 28 '14 at 06:32
  • @SimonJenkins Problem is: applicationDidEnterBackground: will not be called if you leave the app in the way that I described above, nor will it be called if you call the task manager by double clicking. However, my UI obscuring should take place in these cases, too. – Udo Hilwerling Jan 28 '14 at 06:32
  • My first instinct might be to find another way to blur the UI. – GuybrushThreepwood Jan 28 '14 at 09:04

2 Answers2

1

After some testing, I realize that there could be some compromise.

It seems that applicationDidBecomeActive: is only balanced with applicationDidEnterBackground:

In my case this means that the blurring should take place in the latter method. At the end of the day, this means that there will be no screen blurring if the user "leaves" my app by calling the Notification Center or by calling the task manager. However, concerning data security, this should not be a heavy-weighting problem because it's obvious that only the user who had access to my app can perform theses steps.

When the app is left by clicking HOME once, going to standby or by changing the foreground app by the task manager, applicationDidEnterBackground: gets called, and after some testing, it seems it gets enough time from the OS to perform the blurring on the main loop.

However, if you stick to the original problem, this is still unsolved.

0

If the application "becomes active" again before the -applicationWillResignActive() method completes, then it might never have fully resigned the active state, so it makes sense that it might not trigger a call to -applicationDidBecomeActive(), because the active state might not have changed at all.

I would try this:

1) Create a Boolean value called "showingBlur" as a member of you app delegate, and set it to NO in your -applicationDidFinishLaunching method.

2) In -applicationWillResignActive(), BEFORE you start the UI updates, set "showingBlur" to YES.

3) In applicationWillEnterForeground(), check that Boolean, and if it is true, then remove the blur view and set it to false.

OR this:

1) When you make your blur view, also create a foreground timer that deletes both itself the blur view the first time it fires. This way, when you create the timer, you immediate background the app, so it doesn't fire. However, as soon as you open the app, it fires, deletes itself, and removes the blur view.

Hope this helps!

Gazzini
  • 728
  • 7
  • 19
  • The problem is, that applicationWillEnterForeground() doesn't get called either. There is simply no information to the app about the fact that it will go active again. - Will try your second idea! – Udo Hilwerling Jan 27 '14 at 17:21
  • Tried the timer idea - sorry to say that the timer will fire even if the app has already gone to the background. – Udo Hilwerling Jan 27 '14 at 17:32