簡體   English   中英

視圖控制器響應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)

viewDidLoadinit

然后添加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屬性將告訴您哪個特定場景剛剛進入后台。 因此代碼需要檢查通知的窗口場景是否與視圖控制器(或視圖)相關聯。

簡短的一趟 :有關如何獲取UIViewController或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每次都會調用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
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM