How to tell the active view controller when applicationDidBecomeActive is called?
Asked Answered
K

6

25

I feel I am missing a trick here...

I just want to call viewDidLoad or viewDidAppear on the current active view controller when applicationDidBecomeActive gets called, so I can reset some animations or whatever, when the app is started up again from the background. Some of my views don't care, but others really need to know.

I am using Storyboards and my app delegate file has the standard functions - but all with EMPTY bodies. For example, didFinishLaunchingWithOptions just returns YES and does nothing else. Storyboard automagically does everything I guess.

So how can I talk to the current view controller from my rather blank, information free, app delegate?

Kannada answered 27/4, 2012 at 23:36 Comment(1)
There very well may be a simpler way to do this, but I think it would work if you add a property onto your app delegate @property (strong, nonatomic)UIViewController *currentViewController. And then each time you load a view, call back to the delegate to set that property. And then in applicationWillResignActive, save it to NSUserDefaults and check the value when the app becomes active again?Fireproof
S
23

I would recommend using notifications.

In your app delegate's applicationdidBecomeActive method put in this code:

[[NSNotificationCenter defaultCenter] postNotificationName:@"appDidBecomeActive" object:nil];

In your current active view controller's init method subscribe to the notification.

[[NSNotificationCenter defaultCenter] addObserver:self 
                                         selector:@selector(updateStuff)        
                                             name:@"appDidBecomeActive" 
                                           object:nil];

Implement the "updateStuff" method in your controller and you should be able to do whatever you want when the app becomes active.

Standee answered 27/4, 2012 at 23:53 Comment(6)
Thanks, that worked great, seemed the neatest approach of the ones suggested. Though I am slightly concerned about removing the observer correctly. I am using a bunch of ViewControllers modally so they get loaded and unloaded a lot. I call addObserver in -(void)viewDidLoad removeObserver in -(void)viewDidUnload. Seems to work ok. I will test further...Kannada
Don't forget to do [[NSNotificationCenter defaultCenter] removeObserver:self name:@"appDidBecomeActive" object:nil]; in viewDidUnloadKelley
Should not forget to removeObserver in dealloc since viewDidUnload will not get called in all many scenariosBadderlocks
I've tested removing it twice and, removing it redundantly is fine. For that reason I've put it in viewDidUnload and dealloc and I think I've covered all my bases. Thanks all...Kannada
See einsteinx2's answer for a more elegant solution, using the existing UIApplicationDidBecomeActiveNotification notification.Collayer
It will post Notification every time when you coming back from background even if you don't need to post a Notification.Adamsite
L
55

Instead of sending a notification from your app delegate, the OS sends a notification automatically that you can observe:

[[NSNotificationCenter defaultCenter] addObserver:self
                                      selector:@selector(initSongInfo)
                                      name:UIApplicationDidBecomeActiveNotification
                                      object:nil];

and of course make sure to stop observing sometime before or inside your dealloc method, by calling:

[[NSNotificationCenter defaultCenter] removeObserver:self 
                                      name:UIApplicationDidBecomeActiveNotification 
                                      object:nil];
Lenticular answered 12/6, 2012 at 1:53 Comment(3)
This is a better solution than the currently accepted answer by Brandon Brodjeski.Collayer
this is the proper way to do itApophysis
This is a better approach when you are building a framework and you can not add code to the application delegate.Letdown
S
23

I would recommend using notifications.

In your app delegate's applicationdidBecomeActive method put in this code:

[[NSNotificationCenter defaultCenter] postNotificationName:@"appDidBecomeActive" object:nil];

In your current active view controller's init method subscribe to the notification.

[[NSNotificationCenter defaultCenter] addObserver:self 
                                         selector:@selector(updateStuff)        
                                             name:@"appDidBecomeActive" 
                                           object:nil];

Implement the "updateStuff" method in your controller and you should be able to do whatever you want when the app becomes active.

Standee answered 27/4, 2012 at 23:53 Comment(6)
Thanks, that worked great, seemed the neatest approach of the ones suggested. Though I am slightly concerned about removing the observer correctly. I am using a bunch of ViewControllers modally so they get loaded and unloaded a lot. I call addObserver in -(void)viewDidLoad removeObserver in -(void)viewDidUnload. Seems to work ok. I will test further...Kannada
Don't forget to do [[NSNotificationCenter defaultCenter] removeObserver:self name:@"appDidBecomeActive" object:nil]; in viewDidUnloadKelley
Should not forget to removeObserver in dealloc since viewDidUnload will not get called in all many scenariosBadderlocks
I've tested removing it twice and, removing it redundantly is fine. For that reason I've put it in viewDidUnload and dealloc and I think I've covered all my bases. Thanks all...Kannada
See einsteinx2's answer for a more elegant solution, using the existing UIApplicationDidBecomeActiveNotification notification.Collayer
It will post Notification every time when you coming back from background even if you don't need to post a Notification.Adamsite
T
13

Swift version:

You can add this row in your viewDidLoad

NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(viewDidBecomeActive), name: UIApplicationDidBecomeActiveNotification, object: nil)

func viewDidBecomeActive(){
    print("viewDidBecomeActive")
}

Swift 5.x version

NotificationCenter.default.addObserver(self, selector: #selector(viewDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)

@objc func viewDidBecomeActive() {
    print("viewDidBecomeActive")
}
Thornberry answered 8/7, 2016 at 13:19 Comment(1)
swift 4 and 5 NotificationCenter.default.addObserver(self, selector: #selector(viewDidBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)Magically
M
2

Ok so it's pretty catastrophic.

You guys have to pay attention to events registration/unregistration cause you can cause memory leaks.

To make everything work you need to set a flag which knows what's the registration status: either you signed to background events or not. Notice that you need to register to the events when the view controller is seen by the user (if he came from a different one) or if he came from the home screen to your view controller.

You also need to unregister when you leave the view controller to a different one.

In short:

Swift 4:

private var registeredToBackgroundEvents = false

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    registerToBackFromBackground()
}

/// register to back from backround event
private func registerToBackFromBackground() {
    if(!registeredToBackgroundEvents) {
        NotificationCenter.default.addObserver(self, 
        selector: #selector(viewDidBecomeActive), 
        name: UIApplication.didBecomeActiveNotification, object: nil)
        registeredToBackgroundEvents = true
    }
}

/// unregister from back from backround event
private func unregisterFromBackFromBackground() {
    if(registeredToBackgroundEvents) {
        NotificationCenter.default.removeObserver(self, 
        name: UIApplication.didBecomeActiveNotification, object: nil)
        registeredToBackgroundEvents = false
    }

}

@objc func viewDidBecomeActive(){
    logicManager.onBackFromStandby()
}


override func viewWillDisappear(_ animated: Bool) {
    unregisterFromBackFromBackground()
}
Mahone answered 30/4, 2019 at 0:11 Comment(0)
B
0

Rather than trying to keep track of which ViewController is current, you could send a NSNotification from your AppDelegate and subscribe to it in your ViewController.. That way the view controller keeps track of whether or not it needs to call viewDidAppear.

Badderlocks answered 27/4, 2012 at 23:50 Comment(1)
Yes this is what I've gone with. Makes total sense as most of my controllers don't need to register.Kannada
S
0

your AppDelegate will have a window property, that window will have a rootViewController property. You can find your viewController here.

If you are using a TabBarController, the rootviewcontroller will be the tabbarcontroller, and you can call the tabbarcontroller's selectedViewController to get the current viewController.

UIViewController *rootViewController = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
if ([rootViewController isKindOfClass:[UITabBarController Class]])
    rootViewController = ((UITabBarController *)rootViewController).selectedViewController;
else if ([rootViewController isKindOfClass:[UINavigationController Class]])
    rootViewController = ((UINavigationController *)rootViewController).topViewController;

[rootViewController viewDidAppear];

If you have a more complex view hierarchy with navigation controllers, or modal views, you can call on presentedViewController, or topViewController.

Streaky answered 28/4, 2012 at 0:4 Comment(2)
This window property is not working for me, it gives me this error: Use of undeclared identifier 'window'. Can you please update the answer because I really need to know which view is on the screen and then update it.Toolmaker
Thanks for the update, I changed it and it throws an exception as follow: unrecognized selector sent to instance :( for this line of code: UIViewController *vc = tabbarController.selectedViewController;Toolmaker

© 2022 - 2024 — McMap. All rights reserved.