我有一个支持 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
后才能获取视图控制器的场景,因此您不能使用任何init
、viewDidLoad
甚至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
}