Методы делегата приложения не вызываются в iOS 13

Я использую Xcode 11 и создаю приложение для iOS 13. В новом проекте, который я создал в Xcode, я добавил методы делегата для UIApplicationDelegate. В новом шаблоне проекта "Single View App" их не было. Проблема в том, что ни один из методов делегата, кроме -application:didFinishLaunchingWithOptions: звонят. Вот мой делегат приложения:

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    NSLog(@"application:didFinishLaunchingWithOptions:");
    return YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    NSLog(@"applicationDidEnterBackground:");
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    NSLog(@"applicationWillEnterForeground:");
}
#pragma mark - UISceneSession lifecycle

- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
    return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
}

- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {
}

@end

1 ответ

В iOS 13 появился новый способ отправки событий жизненного цикла приложения. Вместо того, чтобы проходить через UIApplicationDelegate они проходят через UIWindowSceneDelegate который является UISceneDelegate суб-протокол. UISceneDelegate методы перечислены в документации API.

Это все для поддержки нескольких окон в iOS 13. Более подробная информация содержится в сеансе 212 WWDC 2019 " Представление нескольких окон на iPad ". Техническая информация начинается около 14:30 и представлена ​​человеком с очень блестящими высокими вершинами.

Если у вас есть " Манифест сцены приложения " в вашем Info.plist, и ваш представитель приложения имеет configurationForConnectingSceneSession метод UIApplication не будет отправлять фоновые и передние сообщения жизненного цикла делегату приложения (applicationDidBecomeActive, applicationWillResignActive, applicationDidEnterBackground, applicationWillEnterForeground). Делегат приложения все равно получит willFinishLaunchingWithOptions: а также didFinishLaunchingWithOptions:,

Если вы хотите вернуть старое поведение, вам нужно

  1. Удалите запись "Манифест сцены приложения" из Info.plist приложения.
  2. Прокомментируйте или удалите application:configurationForConnectingSceneSession:options: метод (или Свифт application(_:configurationForConnecting:options:) функция)
  3. Добавьте свойство окна обратно вашему делегату приложения (@property (strong, nonatomic) UIWindow *window;)

Кроме того, откройте файл SceneDelegate, созданный XCode, и используйте там новые методы жизненного цикла:

- (void)sceneDidBecomeActive:(UIScene *)scene {
}
- (void)sceneWillResignActive:(UIScene *)scene {
}
... etc

Можно использовать новый UIScene вещи жизненного цикла без принятия поддержки нескольких окон, установив "Включить несколько окон" ("UIApplicationSupportsMultipleScenes") на "НЕТ" в Info.plist (это значение по умолчанию для новых проектов). Таким образом, вы можете начать применять новый API более мелкими шагами.

Вы можете видеть, что имена методов делегата сцены близко совпадают с именами делегатов приложения. Одна непонятная вещь заключается в том, что методы делегата приложения не являются устаревшими, поэтому вы не получите предупреждение, если у вас есть и делегат приложения, и методы делегата сцены, но будет вызываться только один.

Другие вещи, которые UISceneDelegate принимает на себя действия пользователя (continueUserActivity: и т. д.), государственное восстановление (stateRestorationActivityForScene: и т. д.), вопросы в строке состояния и открытые URL. (Я не уверен, заменяют ли они методы делегата приложения). Он также имеет аналогичные уведомления для событий жизненного цикла.

С сессии WWDC, некоторые изображения для вас:

Эквиваленты функций для Swift:

Классовые обязанности:

Жизненный цикл приложения и сцены - это не одно и то же!

На мой взгляд, отключение вызовов методов изменения состояния приложения (а также отправка уведомлений об изменении состояния приложения при изменении состояния каждой сцены) является ошибкой, хотя было понятное намерение заставить программистов адаптироваться к жизненному циклу новых сцен.

Вот шаблон делегата сцены, восстанавливающий ожидаемые вызовы методов изменения состояния приложения делегата приложения:

@available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    
    func sceneWillResignActive(_ scene: UIScene) {
        
        if !UIApplication.shared.connectedScenes.contains(where: { $0.activationState == .foregroundActive && $0 != scene }) {
            UIApplication.shared.delegate?.applicationWillResignActive?(.shared)
        }
    }
    
    
    func sceneDidEnterBackground(_ scene: UIScene) {
        
        if !UIApplication.shared.connectedScenes.contains(where: { $0.activationState == .foregroundActive || $0.activationState == .foregroundInactive }) {
            UIApplication.shared.delegate?.applicationDidEnterBackground?(.shared)
        }
    }
    
    
    func sceneWillEnterForeground(_ scene: UIScene) {
        
        if !UIApplication.shared.connectedScenes.contains(where: { $0.activationState == .foregroundActive || $0.activationState == .foregroundInactive }) {
            UIApplication.shared.delegate?.applicationWillEnterForeground?(.shared)
        }
    }
    
    
    func sceneDidBecomeActive(_ scene: UIScene) {
        
        if !UIApplication.shared.connectedScenes.contains(where: { $0.activationState == .foregroundActive && $0 != scene }) {
            UIApplication.shared.delegate?.applicationDidBecomeActive?(.shared)
        }
    }
}

SceneDelegate.swift

Эта ветка мне помогла:

Контроллер представления отвечает на уведомления делегата приложения в iOS 12, но не в iOS 13

Цель C:

if (@available(iOS 13.0, *)) {
    [[NSNotificationCenter defaultCenter] addObserver:self 
          selector:@selector(appWillResignActive:) 
          name:UISceneWillDeactivateNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self 
          selector:@selector(appDidBecomeActive:) 
          name:UISceneDidActivateNotification object:nil];

}
else {
    [[NSNotificationCenter defaultCenter] addObserver:self 
          selector:@selector(appWillResignActive:) 
          name:UIApplicationWillResignActiveNotification object:nil];


    [[NSNotificationCenter defaultCenter]addObserver:self
          selector:@selector(appDidBecomeActive:)
          name:UIApplicationDidBecomeActiveNotification
                                              object:nil];
}
Другие вопросы по тегам