我有一个支持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模拟器/设备上使用。
我需要进行哪些更改?
答案 0 :(得分:9)
在iOS 13下支持场景时,不再调用许多UIApplicationDelegate
生命周期方法。 UISceneDelegate
中现在有相应的生命周期方法。这意味着需要在iOS 13下收听UIScene.didEnterBackgroundNotification
通知。您可以在Managing Your App's Life Cycle页面的文档中找到更多详细信息。
您需要将通知观察者代码更新为:
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
属性将告诉您哪个特定场景刚刚进入背景。因此,代码需要检查以查看通知的窗口场景是否为与视图控制器(或视图)关联的场景。
简要介绍:有关如何获取UIViewController或UIView的UIScene的详细信息,请参见this answer。 (这并不像您希望的那么简单。)
这需要更新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
}