4

I add an imageView to my app's main window in applicationDidEnterBackground:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
        UIImageView *splash = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"splashimage.png"]];
        splash.frame = self.window.bounds;
        [self.window addSubview:splash];
}

I expect that when I put the app into the background by pressing the device's home button, then viewing the task manager by double tapping the home button, I will see the splashimage.png displayed. But it seems that the screenshot that is taken when the app goes into the background does not include this overlay imageView. I thought that it would, since I added the imageView with no animation.

Why might this might be happening?

UPDATE: Even if I hide my window in applicationDidEnterBackground, I can still see the full window, unhidden, in the task manager after the app is put into the background state. The window only becomes hidden a moment after the app returns from the background, when I press the home button.

- (void)applicationDidEnterBackground:(UIApplication *)application
{
        //this does not work either!
        self.window.hidden = YES;
}

UPDATE: I do understand, by the way, that applicationDidEnterBackground has 5 seconds to complete. I am now testing this with only

self.window.hidden = YES;

in applicationDidEnterBackground and nothing in applicationWillResignActive, and the window still is not hidden when the app goes into the background; only when it returns to the foreground. So this is telling me that there must be something else, somewhere in my app, that is not allowing this to happen in applicationDidEnterBackground. By the way, if I move the

self.window.hidden = YES;

to applicationWillResignActive, the window IS hidden when the app goes into the background. But I am trying to figure out what will inhibit an app from completing this single, simple, non-animated task in applicationDidEnterBackground. Any thoughts appreciated.

UPDATE: This particular issue has something to do with using a BannerViewController (iAd). I am using it in a UITabBarController. Not sure yet what exactly the issue is or if it is also related to its use within UITabBarController.

UPDATE: I think the issue is not related to the UITabBarController, but in general the BannerViewController (iAd). Now to understand why...

UPDATE: This line in BannerViewController.m is causing the issue:

[self.view addSubview:bannerView]

in this method:

- (void)viewDidLayoutSubviews
{
    CGRect contentFrame = self.view.bounds, bannerFrame = CGRectZero;
    ADBannerView *bannerView = [BannerViewManager sharedInstance].bannerView;
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_6_0
    NSString *contentSizeIdentifier;
    // If configured to support iOS <6.0, then we need to set the currentContentSizeIdentifier in order to resize the banner properly.
    // This continues to work on iOS 6.0, so we won't need to do anything further to resize the banner.
    if (contentFrame.size.width < contentFrame.size.height) {
        contentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
    } else {
        contentSizeIdentifier = ADBannerContentSizeIdentifierLandscape;
    }
    bannerFrame.size = [ADBannerView sizeFromBannerContentSizeIdentifier:contentSizeIdentifier];
#else
    // If configured to support iOS >= 6.0 only, then we want to avoid currentContentSizeIdentifier as it is deprecated.
    // Fortunately all we need to do is ask the banner for a size that fits into the layout area we are using.
    // At this point in this method contentFrame=self.view.bounds, so we'll use that size for the layout.
    bannerFrame.size = [_bannerView sizeThatFits:contentFrame.size];
#endif

    if (bannerView.bannerLoaded) {
        contentFrame.size.height -= bannerFrame.size.height;
        bannerFrame.origin.y = contentFrame.size.height;
    } else {
        bannerFrame.origin.y = contentFrame.size.height;
    }
    _contentController.view.frame = contentFrame;
    // We only want to modify the banner view itself if this view controller is actually visible to the user.
    // This prevents us from modifying it while it is being displayed elsewhere.
    if (self.isViewLoaded && (self.view.window != nil)) {
        [self.view addSubview:bannerView];
        bannerView.frame = bannerFrame;
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_6_0
        bannerView.currentContentSizeIdentifier = contentSizeIdentifier;
#endif
    }
}

Not exactly sure why or what, if anything can be done to fix it if I still want to use BannerViewController.

UPDATE: from to the accepted answer below, here is what solved this problem for me (bannerVC is a reference to the BannerViewController).

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

    [bannerVC.view snapshotViewAfterScreenUpdates:YES];
}

UPDATE: I realize that this code really should be in applicationDidEnterBackground. But, it seems with AdBannerView that there is no way to stop the animations in time so that this can happen. So for now, in my understanding, applicationWillResignActive is all that is left, but it leaves the user with a 'less-than' experience. I would appreciate any suggestions on how to stop the AdBannerView animations so that the snapshot can be shown in applicationDidEnterBackground and not only when returning from the background.

UPDATE: To replicate the issue:

  1. Download iAdSuite
  2. Open TabbedBanner example.
  3. Add the code below to AppDelegate:
  4. Run the app. (frames are messed up because it hasn't been updated but this example will still show the problem)
  5. Tap the home button once to put the app into the background.
  6. Double-tap the home button to see the app in the task manager.

If you have left the code in applicationWillResignActive commented and the code in applicationDidEnterBackground uncommented, you will not see a blue splash screen in the task manager. But if you have commented the code in applicationDidEnterBackground and uncomment the code in applicationWillResignActive, you should see the blue splash screen in the task manager. This is not desirable however. The issue is how to display the splash screen in the applicationDidEnterBackground method.

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

    //works - but undesirable
    //[self addDummyView];
    //[_tabBarController.view snapshotViewAfterScreenUpdates:YES];
}

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

    //does not work - needs to work to show dummyview only when application actually goes into the background
    [self addDummyView];
    [_tabBarController.view snapshotViewAfterScreenUpdates:YES];
}

- (void)addDummyView {

    UIView *topView = _tabBarController.view;
    UIView *colorView = [[UIView alloc] initWithFrame:topView.frame];
    [colorView setBackgroundColor:[UIColor blueColor]];
    [topView addSubview:colorView];
    [topView bringSubviewToFront:colorView];
}
SAHM
  • 4,078
  • 7
  • 41
  • 77

3 Answers3

2

When the applicationDidEnterBackground method returns, a snapshot of the current view of the app is taken to be used for the multitasking view. The reason that you are not seeing the update is because the snapshot is taken before the drawing cycle. Even if you call setNeedsDisplay the snapshot still is taken before.

To wait until the view updates (after you have added your subview) call this:

[self.view snapshotViewAfterScreenUpdates:YES];

with a value of YES. This forces your changes to render first. You can read more about handling stuff like this in apple docs here: https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/StrategiesforHandlingAppStateTransitions/StrategiesforHandlingAppStateTransitions.html#//apple_ref/doc/uid/TP40007072-CH8-SW27

Max
  • 1,143
  • 7
  • 7
  • Where do I call that - within applicationDidEnterBackground? From looking at the docs it seems like I need to call it from a ViewController but I'm not sure which one that would be or where I would add this code. I will certainly try to see if this can help me with my issue, now that I have pinpointed a little better where it is coming from. – SAHM Jun 05 '15 at 23:56
  • 2
    I think you can use this from app delegate: [[[UIApplication sharedApplication].windows firstObject] snapshotViewAfterScreenUpdates:YES]; .Otherwise listen for the notification from a view controller. – Max Jun 06 '15 at 00:15
  • I see that I can obtain a view from this method (it returns a view) but I am not sure how I can substitute this view for the snapshot view used when the app goes into the background. Not sure this is the answer to my problem but I will keep checking into it until I can rule it out for sure. – SAHM Jun 06 '15 at 00:42
  • The documentation on this is.. lacking. – SAHM Jun 06 '15 at 01:24
  • I got it! Thank you. Use the code in applicationWillResignActive, and call snapshotViewAfterScreenUpdates:YES for the view of the bannerViewController. [bannerVC.view snapshotViewAfterScreenUpdates:YES]; – SAHM Jun 06 '15 at 01:36
  • I realize that applicationWillResignActive is not the best place for this code (sometimes we really don't want to throw up a screenshot there, especially when applicationDidEnterBackground will not eventually be called). But I cannot figure out a way to make this method work in applicationDidEnterBackground while keeping the AdBannerView. Any further help would be appreciated. – SAHM Jun 08 '15 at 19:36
  • I did some testing and could not find a way to consistently display the background using applicationDidEnterBackground. It definitely has to do with the behavior of AdBannerView. You could switch to a different view controller (one without an AdBannerView) - I made an app with two tabs, one with banner and one without. If I was on the one without the banner presenting the view in applicationDidEnterBackground works fine. Or you can make applicationWillResignActive work for your purposes. – Max Jun 10 '15 at 17:50
  • So AdBannerView can't be used if someone wants to hide sensitive user data in the background (without hiding it unnecessarily and awkwardly whenever the application resigns active). I find this hard to believe in theory, and it leaves me very frustrated. Thanks for trying. – SAHM Jun 11 '15 at 00:02
1

You can get you answer here: Show splash screen when application enters background.

Keep your code in applicationWillResignActive.

Community
  • 1
  • 1
sschunara
  • 2,285
  • 22
  • 31
  • I think I already mentioned that I can already accomplish this in applicationWillResignActive, and without the NSRunLoop hack. This provides a poor user experience. – SAHM Jun 15 '15 at 20:43
  • Yes I thought this link would help you to solve your problem so suggested that, please ignore it if it's not helpful. My intense was just to help you. thank you – sschunara Jun 16 '15 at 05:51
0

Your code is fine. You should know that applicationDidEnterBackground is not called when you double tap the home button. It is called when you switch to another app or go to springboard.

So, you should press the home button only once, then double tap it and the screenshot of your app should have the image view. I replicated your use case in a test project and it works for me.

If it still does not work, you should see if the image of the image view is not nil. Oh, and please don't hide the app window.

Teo
  • 3,394
  • 11
  • 43
  • 73
  • Thanks for your comment. But I said above that I put the application into the background by first "pressing the device's home button, then viewing the task manager by double tapping the home button". Also I am only hiding the app window for testing. I would never actually do that in an app. Any more thoughts? – SAHM Jun 05 '15 at 13:13
  • Try setting a breakpoint on ` [self.window addSubview:splash]` and test if the imageView image is not nill – Teo Jun 05 '15 at 13:16
  • It's not nil. And, as I also mentioned, I can't even successfully hide the app window in this method. So I am thinking the problem must be elsewhere in the app. Maybe related to the fact that I am using a storyboard? Maybe something having to do with memory? – SAHM Jun 05 '15 at 13:18
  • In the test project I am also using storyboards, so that is not the problem. Try setting breakpoints in the viewDidEnterBackground and go step by step and see if the imageView is correctly being added (on front not on back). Also, just for debug's sake make your image view's background red and see if you see anything new. – Teo Jun 05 '15 at 13:28
  • I have checked everything related to the view and it is fine. At this point I am focusing on the fact that I cannot even set the window to hidden in the applicationDidEnterBackground method. That shows me that my problem is somewhere outside of the actual method. – SAHM Jun 05 '15 at 13:31
  • Okay. It has something to do with using a BannerViewController (iAd). I am using it in a TabBarController. Still have more digging to do but I thought I'd give an update. – SAHM Jun 05 '15 at 23:06