视图控制器在 iOS 12 中响应应用程序委托通知,但在 iOS 13 中不响应
View controller responds to app delegate notifications in iOS 12 but not in 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 simulator/device.
我需要做哪些改变?
支持场景时iOS 13、UIApplicationDelegate
lifecycle methods are no longer called. There are now corresponding lifecycle methods in the UISceneDelegate
. This means there is a need to listen to the UIScene.didEnterBackgroundNotification
notification under iOS 13. You can find more details in the documentation at the 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
方法。
但是如果您的应用支持多个 windows。
如果您的应用程序的用户打开了多个 windows 您的应用程序,那么即使给定的视图控制器仍在前景或者如果一直在背景中。
在可能的情况下,您只希望刚刚放入后台的 window 响应事件,您需要添加一个额外的检查。通知的object
属性会告诉你具体是哪个场景刚刚进入后台。因此代码需要检查通知的 window 场景是否与视图控制器(或视图)关联。
简要说明:有关如何获取 UIViewController 或 UIView 的 UIScene 的详细信息,请参阅 。 (这并不像你希望的那么简单)。
这需要对 didEnterBackground
方法进行如下更新:
@objc func didEnterBackground(_ notification: NSNotification) {
if #available(iOS 13.0, *) {
// This requires the extension found at:
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
注册时,您可以将自己的window 场景指定为object
参数的参数。那么didEnterBackground
方法只会为你自己的window场景调用。
这样做的诀窍是在注册通知时获得自己的 window 场景。由于你只能在 viewDidAppear
至少被调用一次后才能获得视图控制器的场景,你不能使用任何 init
、viewDidLoad
甚至 viewWillAppear
。那些都太早了。
由于 viewDidAppear
可以被多次调用,您最终将每次都调用 addObserver
,这是一个问题,因为这样您的处理程序将针对单个事件被多次调用。所以一种想法是注销 viewDidDisappear
中的观察者。但是现在这有一个问题,如果其他视图控制器正在覆盖它,则您的视图控制器不会被调用。所以技巧是在 viewDidAppear
中添加观察者,但只是第一次为视图控制器的特定实例调用它。
如果您可以等到 viewDidAppear
,那么首先您需要在 class 中添加一个 属性 以跟踪它是否已被查看。
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:
if (notification.object != self.scene) {
return; // not my scene
} // else my own scene
} // else iOS 12
// Do stuff
}
如果你想使用 viewDidAppear
并且有一个更简单的 didEnterBackground
:
将实例变量添加到您的 class:
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
}
我有一个应用程序支持 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 simulator/device.
我需要做哪些改变?
支持场景时iOS 13、UIApplicationDelegate
lifecycle methods are no longer called. There are now corresponding lifecycle methods in the UISceneDelegate
. This means there is a need to listen to the UIScene.didEnterBackgroundNotification
notification under iOS 13. You can find more details in the documentation at the 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
方法。
但是如果您的应用支持多个 windows。
如果您的应用程序的用户打开了多个 windows 您的应用程序,那么即使给定的视图控制器仍在前景或者如果一直在背景中。
在可能的情况下,您只希望刚刚放入后台的 window 响应事件,您需要添加一个额外的检查。通知的object
属性会告诉你具体是哪个场景刚刚进入后台。因此代码需要检查通知的 window 场景是否与视图控制器(或视图)关联。
简要说明:有关如何获取 UIViewController 或 UIView 的 UIScene 的详细信息,请参阅
这需要对 didEnterBackground
方法进行如下更新:
@objc func didEnterBackground(_ notification: NSNotification) {
if #available(iOS 13.0, *) {
// This requires the extension found at:
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
注册时,您可以将自己的window 场景指定为object
参数的参数。那么didEnterBackground
方法只会为你自己的window场景调用。
这样做的诀窍是在注册通知时获得自己的 window 场景。由于你只能在 viewDidAppear
至少被调用一次后才能获得视图控制器的场景,你不能使用任何 init
、viewDidLoad
甚至 viewWillAppear
。那些都太早了。
由于 viewDidAppear
可以被多次调用,您最终将每次都调用 addObserver
,这是一个问题,因为这样您的处理程序将针对单个事件被多次调用。所以一种想法是注销 viewDidDisappear
中的观察者。但是现在这有一个问题,如果其他视图控制器正在覆盖它,则您的视图控制器不会被调用。所以技巧是在 viewDidAppear
中添加观察者,但只是第一次为视图控制器的特定实例调用它。
如果您可以等到 viewDidAppear
,那么首先您需要在 class 中添加一个 属性 以跟踪它是否已被查看。
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:
if (notification.object != self.scene) {
return; // not my scene
} // else my own scene
} // else iOS 12
// Do stuff
}
如果你想使用 viewDidAppear
并且有一个更简单的 didEnterBackground
:
将实例变量添加到您的 class:
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
}