182

I have a View Controller in which my value is 0 (label) and when I open that View Controller from another ViewController I have set viewDidAppear to set value 20 on label. It works fine but when I close my app and than again I open my app but the value doesn't change because viewDidLoad, viewDidAppear and viewWillAppear nothing get called. How can I call when I open my app. Do I have to do anything from applicationDidBecomeActive?

Marcus Leon
  • 55,199
  • 118
  • 297
  • 429
Zohaib
  • 2,845
  • 2
  • 23
  • 33
  • 1
    You could post a local notification when application become active and add your view controller as observer and update values. – Adil Soomro Apr 07 '13 at 16:01

8 Answers8

316

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
danh
  • 62,181
  • 10
  • 95
  • 136
140

Using Objective-C

You should register a UIApplicationWillEnterForegroundNotification in your ViewController's viewDidLoad method and whenever app comes back from background you can do whatever you want to do in the method registered for notification. ViewController's viewWillAppear or viewDidAppear won't be called when app comes back from background to foreground.

-(void)viewDidLoad{

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doYourStuff)

  name:UIApplicationWillEnterForegroundNotification object:nil];
}

-(void)doYourStuff{

   // do whatever you want to do when app comes back from background.
}

Don't forget to unregister the notification you are registered for.

-(void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

Note if you register your viewController for UIApplicationDidBecomeActiveNotification then your method would be called every time your app becomes active, It is not recommended to register viewController for this notification .

Using Swift

For adding observer you can use the following code

 override func viewDidLoad() {
    super.viewDidLoad()

     NotificationCenter.default.addObserver(self, selector: "doYourStuff", name: UIApplication.willEnterForegroundNotification, object: nil)
 }

 func doYourStuff(){
     // your code
 }

To remove observer you can use deinit function of swift.

deinit {
    NotificationCenter.default.removeObserver(self)
}
Brad Reed
  • 443
  • 7
  • 16
nsgulliver
  • 12,655
  • 23
  • 43
  • 64
  • 4
    Yes it is :) sometimes it is difficult to find answers :) – nsgulliver Apr 07 '13 at 16:59
  • @nsgulliver Do i have to manually invoke unregister the notification -(void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } . Will app do it for me? – ios Sep 14 '17 at 05:10
43

Swift 3.0 ++ version

In your viewDidLoad, register at notification center to listen to this opened from background action

NotificationCenter.default.addObserver(self, selector:#selector(doSomething), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)
        

Then add this function and perform needed action

func doSomething(){
    //...
}

Finally add this function to clean up the notification observer when your view controller is destroyed.

deinit {
    NotificationCenter.default.removeObserver(self)
}
Community
  • 1
  • 1
Fangming
  • 24,551
  • 6
  • 100
  • 90
20

Swift 4.2. version

Register with the NotificationCenter in viewDidLoad to be notified when the app returns from background

NotificationCenter.default.addObserver(self, selector: #selector(doSomething), name: UIApplication.willEnterForegroundNotification, object: nil)

Implement the method that should be called.

@objc private func doSomething() {
    // Do whatever you want, for example update your view.
}

You can remove the observer once the ViewController is destroyed. This is only required below iOS9 and macOS 10.11

deinit {
    NotificationCenter.default.removeObserver(self)
}
gebirgsbärbel
  • 2,327
  • 1
  • 22
  • 38
  • 1
    FYI I'm pretty sure you don't need to bother removing observers anymore these days... – Fattie Aug 01 '19 at 00:02
  • @Fattie, I was hoping you'd provide a link to some official Apple documentation. Anyways, here it is: https://developer.apple.com/documentation/foundation/notificationcenter/1407263-removeobserver#discussion. "If your app targets iOS 9.0 and later or macOS 10.11 and later, and you used addObserver(_:selector:name:object:) to create your observer, you do not need to unregister the observer." – Xavier L. May 09 '23 at 23:48
3

Just have your view controller register for the UIApplicationWillEnterForegroundNotification notification and react accordingly.

andreag
  • 881
  • 8
  • 12
  • How will i do that? i have called my viewController in applicationDidBecomeActive but. it overlap viewController or its fine to do that? – Zohaib Apr 07 '13 at 16:15
  • 2
    Don't call your viewController in applicationDidBecomeActive (which is wrong anyway because it gets called multiple times). Register for the notification in your `viewDidLoad` like @nsgulliver suggested. Your `viewDidAppear` will also call `doYourStuff` to set your label with the desired value. – andreag Apr 07 '13 at 16:54
3

I think registering for the UIApplicationWillEnterForegroundNotification is risky as you may end up with more than one controller reacting to that notification. Nothing garanties that these controllers are still visible when the notification is received.

Here is what I do: I force call viewDidAppear on the active controller directly from the App's delegate didBecomeActive method:

Add the code below to - (void)applicationDidBecomeActive:(UIApplication *)application

UIViewController *activeController = window.rootViewController;
if ([activeController isKindOfClass:[UINavigationController class]]) {
    activeController = [(UINavigationController*)window.rootViewController topViewController];
}
[activeController viewDidAppear:NO];
Erwan
  • 3,733
  • 30
  • 25
  • 8
    It IS guaranteed if the controller unregisters (as it should) for the UIApplicationWillEnterForegroundNotification in viewWillDisappear instead of in dealloc. Calling viewDidAppear explicitly looks like a hack to me, it breaks semantics (personal view) and can confuse people (from experience). – joakim Apr 01 '15 at 23:57
3

try adding this in AppDelegate applicationWillEnterForeground.

func applicationWillEnterForeground(_ application: UIApplication) {        
    // makes viewWillAppear run
    self.window?.rootViewController?.beginAppearanceTransition(true, animated: false)
    self.window?.rootViewController?.endAppearanceTransition()
}
Axel Guilmin
  • 11,454
  • 9
  • 54
  • 64
richc
  • 1,648
  • 5
  • 20
  • 48
2

As per Apple's documentation:

(void)beginAppearanceTransition:(BOOL)isAppearing animated:(BOOL)animated;

Description:
Tells a child controller its appearance is about to change. If you are implementing a custom container controller, use this method to tell the child that its views are about to appear or disappear. Do not invoke viewWillAppear:, viewWillDisappear:, viewDidAppear:, or viewDidDisappear: directly.

(void)endAppearanceTransition;

Description:

Tells a child controller its appearance has changed. If you are implementing a custom container controller, use this method to tell the child that the view transition is complete.

Sample code:

(void)applicationDidEnterBackground:(UIApplication *)application
{

    [self.window.rootViewController beginAppearanceTransition: NO animated: NO];  // I commented this line

    [self.window.rootViewController endAppearanceTransition]; // I commented this line

}

Question: How I fixed?

Ans: I found this piece of lines in application. This lines made my app not recieving any ViewWillAppear notification's. When I commented these lines it's working fine.

konstantin_doncov
  • 2,725
  • 4
  • 40
  • 100
Lakshmi
  • 21
  • 3