当applicationDidBecomeActive被调用时如何告诉当前的视图控制器?

25

我感觉我在这里错过了一些技巧...

当应用程序从后台重新启动时,我想要调用当前活动视图控制器的viewDidLoad或viewDidAppear方法,以便重置一些动画或其他内容。我的某些视图不在意,但其他视图确实需要知道。

我正在使用Storyboards,并且我的应用程序代理文件具有标准函数 - 但所有函数主体都是EMPTY的。例如,didFinishLaunchingWithOptions只返回YES而没有做其他任何事情。我猜Storyboard自动完成了一切。

那么,我如何从我的空白、无信息的应用程序代理中与当前视图控制器通信呢?


可能有更简单的方法来完成这个任务,但我认为如果您在应用程序委托中添加一个属性@property (strong, nonatomic)UIViewController *currentViewController,然后每次加载视图时回调委托以设置该属性,再在applicationWillResignActive中将其保存到NSUserDefaults并在应用程序再次变为活动状态时检查该值,这种方法也可以实现。 - geraldWilliam
6个回答

55

与其从您的应用程序委托发送通知,操作系统会自动发送通知,您可以观察到:

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

当然,在dealloc方法中或之前要确保停止观察,方法是调用:

[[NSNotificationCenter defaultCenter] removeObserver:self 
                                      name:UIApplicationDidBecomeActiveNotification 
                                      object:nil];

3
这个方案比Brandon Brodjeski当前接受的答案更好。 - MattyG
2
这是正确的做法。 - Collin Thomas
1
当您构建框架并且无法向应用程序委托添加代码时,这是更好的方法。 - Buddhisthead

23

我建议使用通知。

在你的应用程序代理的applicationdidBecomeActive方法中添加以下代码:

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

在您当前活动的视图控制器的init方法中订阅通知。

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

在您的控制器中实现“updateStuff”方法,这样当应用程序变为活动状态时,您就可以自由操作。


谢谢,那个方法很好用,看起来是建议中最简洁的。虽然我稍微担心正确地移除观察者。我使用了一堆模态视图控制器,所以它们会被频繁加载和卸载。我在-(void)viewDidLoad中调用addObserver,在-(void)viewDidUnload中调用removeObserver。看起来还不错。我会进一步测试... - David John
不要忘记在 viewDidUnload 中执行 [[NSNotificationCenter defaultCenter] removeObserver:self name:@"appDidBecomeActive" object:nil];。 - Maksim
2
在dealloc中不要忘记移除观察者,因为viewDidUnload在许多情况下都不会被调用。 - Joseph DeCarlo
1
我已经测试过两次删除它,多余地删除是可以的。因此,我将其放在viewDidUnload和dealloc中,我认为我已经考虑到了所有情况。谢谢大家... - David John
4
请参考einsteinx2的回答,使用现有的UIApplicationDidBecomeActiveNotification通知来获得更加优雅的解决方案。 - MattyG
每次从后台返回时,它都会发布通知,即使您不需要发布通知。 - Nisar Ahmad

13

Swift版本:

你可以在viewDidLoad函数中添加这一行代码

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

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

Swift 5.x 版本

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

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

Swift 4和5 NotificationCenter.default.addObserver(self, selector: #selector(viewDidBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil) - xhinoda

2

好的,这是相当灾难性的。

您需要注意事件注册/注销,因为这可能会导致内存泄漏。

为了使一切正常工作,您需要设置一个标志,该标志知道注册状态:无论您是否已经注册到后台事件。请注意,当用户查看视图控制器时(如果他来自不同的视图控制器),或者如果他从主屏幕进入您的视图控制器时,您需要注册到事件。

离开视图控制器前,您还需要注销。

简而言之:

Swift 4:

Original Answer翻译成"最初的回答"


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

你的AppDelegate将拥有一个window属性,该窗口将拥有一个rootViewController属性。你可以在这里找到你的viewController。

如果你正在使用TabBarController,则rootviewcontroller将是tabbarcontroller,并且你可以调用tabbarcontroller的selectedViewController来获取当前的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];

如果您有一个更复杂的视图层次结构,包括导航控制器或模态视图,您可以调用presentedViewController或topViewController。


这个窗口属性对我不起作用,它给了我这个错误:使用未声明的标识符“window”。你能否更新答案,因为我真的需要知道屏幕上显示的是哪个视图,然后更新它。 - Hamid
感谢您的更新,我进行了更改,但是出现了以下异常:未识别的选择器发送给实例 :( 出现问题的代码行是:UIViewController *vc = tabbarController.selectedViewController; - Hamid

0

与其试图跟踪当前的ViewController,您可以从AppDelegate发送一个NSNotification,并在您的ViewController中订阅它。这样,视图控制器就可以跟踪它是否需要调用viewDidAppear。


是的,这就是我选择的方案。这样做非常有道理,因为我的大多数控制器都不需要注册。 - David John

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接