View Controller在iOS 12中响应应用程序委托通知,但在iOS 13中不响应 [英] View controller responds to app delegate notifications in iOS 12 but not in iOS 13

查看:62
本文介绍了View Controller在iOS 12中响应应用程序委托通知,但在iOS 13中不响应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have an app that supports iOS 12. I am adding support for iOS 13. I have a view controller that needs to perform a quick action when the app goes to the background.

在iOS 13之前非常简单.添加一行,例如:

Prior to iOS 13 that is simple enough. Add a line such as:

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

中的

init中的

然后添加didEnterBackground方法:

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

在iOS 12及更低版本中,这一切都很好.

That's all good with iOS 12 and earlier.

但是现在有了iOS 13的场景支持,当在iOS 13上运行时,我的通知不会被调用.它仍然可以在iOS 12模拟器/设备上使用.

But now with scene support in iOS 13, my notification isn't being called when run with iOS 13. It still works with an iOS 12 simulator/device.

我需要进行哪些更改?

推荐答案

在iOS 13下支持场景时,许多 UIApplicationDelegate 生命周期方法不再被调用. UISceneDelegate 中现在有相应的生命周期方法.这意味着需要听下面的 UIScene.didEnterBackgroundNotification 通知iOS13.您可以在文档中的管理应用程序的生命周期页面.

When supporting scenes under iOS 13, many of the 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 page.

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

You need to update the notification observer code to:

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版本上.

This allows your view controller (or view) to listen to the correct event depending on which version of iOS it is running under.

两个事件都将调用相同的didEnterBackground方法,具体取决于iOS的版本.

The same didEnterBackground method is called for both events depending on the version of iOS.

但是,如果您的应用程序支持多个窗口,则会增加复杂性.

But there is an added complication if your app supports multiple windows.

如果应用程序的用户打开了应用程序的多个窗口,则即使给定的视图控制器仍在前台或已被关闭,也会向该视图控制器(或视图)的每个副本通知背景事件.一直在后台.

If the user of your app has opened multiple windows of your app, then every copy of this view controller (or view) will be notified of the background event even if the given view controller is still in the foreground or if has been in the background all along.

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

In the likely case you only want the one window that was just put into the background to respond to the event, you need to add an extra check. The object property of the notification will tell you which specific scene has just entered the background. So the code needs to check to see if the notification's window scene is scene associated with the view controller (or view).

简述:请参阅此答案以获取有关如何获取UIScene的详细信息UIViewController或UIView. (这并不像您希望的那样简单.)

Brief side trip: See this answer for details on how to get the UIScene of a UIViewController or UIView. (It's not as straightforward as you would hope).

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

This requires an update to the didEnterBackground method as follows:

@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方法将仅针对您自己的窗口场景被调用.


There is a way to make this a little simpler. When registering with NotificationCenter, you can specify your own window scene as an argument to the object parameter. Then the didEnterBackground method will only be called for your own window scene.

解决这个问题的技巧是在注册通知时获得自己的窗口场景.由于只能在至少调用一次viewDidAppear之后获得视图控制器的场景,因此不能使用任何initviewDidLoad甚至viewWillAppear.这些都为时过早.

The trick with this is getting your own window scene at the time you register for the notification. Since you can only get a view controller's scene after viewDidAppear has been called at least once, you can't use any init, viewDidLoad, or even viewWillAppear. Those are all too early.

由于viewDidAppear可以被多次调用,因此您每次都会调用addObserver,这是一个问题,因为这样您的处理程序将为单个事件多次调用.因此,一个想法是在viewDidDisappear中注销观察者的注册.但这又带来了一个问题,即如果某个其他视图控制器覆盖了它,则不会调用您的视图控制器.因此,欺骗性的方法是在viewDidAppear中添加观察者,但仅在第一次为视图控制器的特定实例调用时才添加观察者.

Since viewDidAppear can be called more than once, you will end up calling addObserver each time and that is a problem because then your handler will get called multiple times for a single event. So one thought is to unregister the observer in viewDidDisappear. But then this now has the problem of your view controller not being called if some other view controller is covering it. So the trick it to add the observer in viewDidAppear but only the first time it is called for a specific instance of the view controller.

如果您可以等到viewDidAppear,则首先需要在类中添加一个属性,以跟踪是否已查看该属性.

If you can wait until viewDidAppear, then first you need to add a property to your class to keep track of whether it's been viewed yet or not.

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可以再次成为旧的简单版本:

And then your didEnterBackground can be the old simple version again:

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


对于Objective-C,代码如下:


For Objective-C, the code is as follows:

注册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:

If you want to use viewDidAppear and have a simpler didEnterBackground:

将实例变量添加到您的类中:

Add an instance variable to your 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
}

这篇关于View Controller在iOS 12中响应应用程序委托通知,但在iOS 13中不响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆