Viewdidappear Is Not Called When Opening App from Background

ViewDidAppear is not called when opening app from background

Curious about the exact sequence of events, I instrumented an app as follows: (@Zohaib, you can use the NSNotificationCenter code below to answer your question).

// AppDelegate.m

- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSLog(@"app will enter foreground");
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
NSLog(@"app did become active");
}

// ViewController.m

- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"view did load");

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
}

- (void)appDidBecomeActive:(NSNotification *)notification {
NSLog(@"did become active notification");
}

- (void)appWillEnterForeground:(NSNotification *)notification {
NSLog(@"will enter foreground notification");
}

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"view will appear");
}

- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"view did appear");
}

At launch, the output looks like this:

2013-04-07 09:31:06.505 myapp[15459:11303] view did load
2013-04-07 09:31:06.507 myapp[15459:11303] view will appear
2013-04-07 09:31:06.511 myapp[15459:11303] app did become active
2013-04-07 09:31:06.512 myapp[15459:11303] did become active notification
2013-04-07 09:31:06.517 myapp[15459:11303] view did appear

Enter the background then reenter the foreground:

2013-04-07 09:32:05.923 myapp[15459:11303] app will enter foreground
2013-04-07 09:32:05.924 myapp[15459:11303] will enter foreground notification
2013-04-07 09:32:05.925 myapp[15459:11303] app did become active
2013-04-07 09:32:05.926 myapp[15459:11303] did become active notification

ViewDidAppear not executed every time app comes to foreground

In addition to viewDidAppear, you can have your view controller observe UIApplication.didBecomeActiveNotification (previously known as UIApplicationDidBecomeActive):

private var observer: NSObjectProtocol?

override func viewDidLoad() {
super.viewDidLoad()

observer = NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: .main) { [weak self] notification in
self?.updateUI()
}
}

deinit {
if let observer = observer {
NotificationCenter.default.removeObserver(observer)
}
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updateUI()
}

private func updateUI() {
// do your UI update stuff here
}

Why does viewWillAppear not get called when an app comes back from the background?

The method viewWillAppear should be taken in the context of what is going on in your own application, and not in the context of your application being placed in the foreground when you switch back to it from another app.

In other words, if someone looks at another application or takes a phone call, then switches back to your app which was earlier on backgrounded, your UIViewController which was already visible when you left your app 'doesn't care' so to speak -- as far as it is concerned, it's never disappeared and it's still visible -- and so viewWillAppear isn't called.

I recommend against calling the viewWillAppear yourself -- it has a specific meaning which you shouldn't subvert! A refactoring you can do to achieve the same effect might be as follows:

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self doMyLayoutStuff:self];
}

- (void)doMyLayoutStuff:(id)sender {
// stuff
}

Then also you trigger doMyLayoutStuff from the appropriate notification:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doMyLayoutStuff:) name:UIApplicationDidChangeStatusBarFrameNotification object:self];

There's no out of the box way to tell which is the 'current' UIViewController by the way. But you can find ways around that, e.g. there are delegate methods of UINavigationController for finding out when a UIViewController is presented therein. You could use such a thing to track the latest UIViewController which has been presented.

Update

If you layout out UIs with the appropriate autoresizing masks on the various bits, sometimes you don't even need to deal with the 'manual' laying out of your UI - it just gets dealt with...

viewDidAppear, but also when app returns from background

First issue: viewDidAppear and viewDidDisappear would be the best places to register/unregister the notifications, for the reason you outlined in your second issue (on first launch, registering UIApplicationDidBecomeActiveNotification in viewWillAppear would cause a duplicate animation.

Edit: If you are just checking for when the app returns from background then you should use UIApplicationWillEnterForegroundNotification then you can use viewDidAppear or viewWillAppear without having to worry about duplicate calls.

viewDidAppear and viewWillDisappear would be the best places to run the animations so the user will actually get to see them.

Second Issue: viewDidAppear/viewWillAppear don't get called when the app returns from background so you don't have to worry about these both being called at the same time.

In some scenarios it would be nice to have an overall 'viewHasDefinitelyAppeared' but there are quite a few instances where you would want them separate, not all apps want users to see animations when they return from background which wouldn't be possible if all the methods where combined in one.

viewDidAppear not firing ever again after app enters foreground

Found it.

While not new to programming I am new to iPhone development. On researching this problem I found it was not recommended to call the viewWillAppear and viewWillDisappear methods manually.

My viewWillDisappear methods resign any keyboards if shown, when my app enters the background it loads a splash screen ready for when the app re-enters the foreground (there is some logic I need to do to work out what the user is shown on restarting the app and I can do this under the splash screen).

As viewWillDisappear is not called when the app goes into the background to make sure no keyboards appeared over my splash screen I was calling viewWillDisapper in the applicationDidEnterBackground method. I guess this also un-registers my events.

By adding viewWillAppear to my applicationDidEnterForeground method my events started firing again. Lesson learned, I will refactor this so I don't call these events manually.

Thanks for the help.

Swift - viewDidLoad getting called when app moves to the background

No it's not expected for this to happen.

Step 1

• Check your AppDelegate class inside AppDelegate.swift

• Find the applicationDidEnterBackground() method.

• If there's anything in there calling the viewDidLoad() method in your ViewController, you've found the issue.

Step 2

• Run your app

• Put a breakpoint inside your viewDidLoad() method

• Put your app into the background

• Did the breakpoint get hit?

• Click the tiny button in the bottom middle area, a small rectangle with an upwards pointing arrow above it

• This will take you to the last place this was called. And you will (hopefully) find the issue.

Step 3

• I have no other ideas. Some more information would be super helpful. Thank you!

Are viewcontroller llfecycle events suppose to be called when app is launched into background?

Yes, it is, sort of. "appear" doesn't mean "the user is about to see it". It means "the view controller hierarchy is being assembled and this view controller's view is going into the hierarchy". The view controller calls should be stable regardless of whether we launch in the foreground or background.

(To see this, imagine the contrary; all hell would break loose! Launching is launching, view controllers are view controllers; you must get the expected events or the app will break.)


But let me make a stronger warning here, while I have the soapbox. Do not make any assumptions about how two different lifetime cycles will interleave with one another. The app lifetime cycle is stable and the view controller lifetime cycle is stable, but how they interleave with one another varies greatly from major system to major system and from architecture to architecture (by "architecture" I mean that that it matters whether you are using a navigation controller etc.).

I've got a lot of history with this. When I first started programming iOS I worked out that this was the interleaved order:

  • application(_:didFinishLaunchingWithOptions:)
  • viewDidLoad
  • viewWillAppear(_:)
  • applicationDidBecomeActive(_:)
  • viewDidAppear(_:)

Relying on that order, I typically used the root view controller's viewDidAppear(_:) to register for UIApplication.didBecomeActiveNotification in order to be notified of subsequent activations of the app.

That worked fine for some years. But iOS 8 brought with it a momentous change: the app delegate now received applicationDidBecomeActive(_:) after the root view controller received viewDidAppear(_:), like this:

  • application(_:didFinishLaunchingWithOptions:)
  • viewDidLoad
  • viewWillAppear(_:)
  • viewDidAppear(_:)
  • applicationDidBecomeActive(_:)

This was a disaster for many of my apps, because the notification I had just registered for in viewDidAppear(_:) arrived immediately.

Then, in iOS 9, the order returned to what it was in iOS 7 and before — knocking my apps into confusion once again. Then, in iOS 11, the order reverted back to what it was in iOS 8!

The moral is that you should not, as I did, rely upon the timing relationship between lifetime events of different objects. What I was doing was always wrong.

viewDidAppear called after applicationDidEnterBackground?

applicationDidEnterBackground: is not the last routine that is executed by the app process before it gets suspended.

According to the description of this method, your app process is not suspended for about the next 5 (five) seconds after this method is called, and keeps running normally.



Related Topics



Leave a reply



Submit