视图控制器在 iOS 12 中响应应用委托通知,但在 iOS 13 中不响应



我有一个支持 iOS 12 的应用程序。我正在添加对 iOS 13 的支持。我有一个视图控制器,当应用程序进入后台时,需要执行快速操作。

在iOS 13之前,这很简单。添加一行,例如:

NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)

viewDidLoad或可能是init.

然后添加didEnterBackground方法:

@objc func didEnterBackground() {
// Do my background stuff
}

对于iOS 12及更早版本来说,这一切都很好。

但是现在有了 iOS 13 中的场景支持,在 iOS 13 上运行时不会调用我的通知。它仍然适用于iOS 12模拟器/设备。

我需要进行哪些更改?

在 iOS 13 下支持场景时,不再调用许多UIApplicationDelegate生命周期方法。UISceneDelegate中现在有相应的生命周期方法。这意味着需要在iOS 13下收听UIScene.didEnterBackgroundNotification通知。您可以在管理应用的生命周期页面的文档中找到更多详细信息。

您需要将通知观察者代码更新为:

if #available(iOS 13.0, *) {
NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground), name: UIScene.didEnterBackgroundNotification, object: nil)
} else {
NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
}

这允许您的视图控制器(或视图(侦听正确的事件,具体取决于它在哪个版本的 iOS 下运行。

根据 iOS 的版本,对这两个事件调用相同的didEnterBackground方法。


但是,如果您的应用支持多个窗口,则会出现额外的复杂性。

如果应用的用户打开了应用的多个窗口,则此视图控制器(或视图(的每个副本都将收到后台事件的通知,即使给定的视图控制器仍位于前台或一直位于后台。

在可能的情况下,您只希望刚刚放入后台的一个窗口响应事件,您需要添加额外的检查。通知的object属性将告诉您哪个特定场景刚刚进入背景。因此,代码需要检查通知的窗口场景是否是与视图控制器(或视图(关联的场景。

简短的附带旅行:有关如何获取UIView控制器或UIView的UIScene的详细信息,请参阅此答案。(它并不像您希望的那么简单(。

这需要更新didEnterBackground方法,如下所示:

@objc func didEnterBackground(_ notification: NSNotification) {
if #available(iOS 13.0, *) {
// This requires the extension found at: https://stackoverflow.com/a/56589151/1226963
if let winScene = notification.object as? UIWindowScene, winScene === self.scene {
return; // not my scene man, I'm outta here
} // else this is my scene, handle it
} // else iOS 12 and we need to handle the app going to the background
// Do my background stuff
}

有一种方法可以使这更简单一些。向NotificationCenter注册时,可以将自己的窗口场景指定为object参数的参数。然后,将仅针对您自己的窗口场景调用didEnterBackground方法。

这样做的诀窍是在注册通知时获取自己的窗口场景。由于您只能在至少调用一次viewDidAppear后才能获取视图控制器的场景,因此您不能使用任何initviewDidLoad甚至viewWillAppear。这些都为时过早。

由于viewDidAppear可以多次调用,因此每次最终都会调用addObserver,这是一个问题,因为这样您的处理程序将针对单个事件多次调用。所以一个想法是在viewDidDisappear中取消注册观察者。但是现在,如果其他视图控制器覆盖了它,则存在视图控制器未被调用的问题。因此,它的诀窍是viewDidAppear中添加观察器,但仅在第一次为视图控制器的特定实例调用它时。

如果可以等到viewDidAppear,那么首先需要向类添加一个属性,以跟踪它是否已被查看。

var beenViewed = false

然后添加viewDidAppear

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !beenViewed {
beenViewed = true
if #available(iOS 13.0, *) {
// Only be notified of my own window scene
NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground), name: UIScene.didEnterBackgroundNotification, object: self.view.window?.windowScene)
} else {
NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
}
}
}

然后你的didEnterBackground可以再次是旧的简单版本:

@objc func didEnterBackground() {
// Do my background stuff
}

对于 Objective-C,代码如下:

viewDidAppear之前注册通知:

if (@available(iOS 13.0, *)) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground:) name:UISceneDidEnterBackgroundNotification object:nil];
} else {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
}

更复杂的didEnterBackground

- (void)didEnterBackground:(NSNotification *)notification {
if (@available(iOS 13.0, *)) {
// This requires the extension found at: https://stackoverflow.com/a/56589151/1226963
if (notification.object != self.scene) {
return; // not my scene
}  // else my own scene
} // else iOS 12
// Do stuff
}

如果您想使用viewDidAppear并拥有更简单的didEnterBackground

将实例变量添加到类中:

BOOL beenViewed;

然后添加viewDidAppear

- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (!beenViewed) {
beenViewed = YES;
if (@available(iOS 13.0, *)) {
// Only be notified of my own window scene
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground) name:UISceneDidEnterBackgroundNotification object:self.view.window.windowScene];
} else {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
}
}

更简单的didEnterBackground

- (void)didEnterBackground {
// Do stuff
}

最新更新