20
votes

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?

6
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? - geraldWilliam

6 Answers

55
votes

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];
23
votes

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.

6
votes

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")
}
1
votes

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()
}
0
votes

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.

0
votes

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.