Darkmode и мультиокна / сцены

Я пытаюсь реализовать темный режим ios13 в многосценном приложении.

К сожалению, когда я закрываю сцену, перетаскивая ее за край экрана, метод traitCollectionDidChange вызывается несколько раз с всегда разными значениями, в результате чего мой пользовательский интерфейс мерцает между темным и светлым режимами.

Что случилось?

Вот моя реализация

func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {  
    super.traitCollectionDidChange(previousTraitCollection)  

    print("THEME instance: \(self)")  

    let currentTraitCollection = self.traitCollection  
    var hasUserInterfaceStyleChanged = false  
    hasUserInterfaceStyleChanged = previousTraitCollection.hasDifferentColorAppearanceCompared(to: currentTraitCollection)  

    print("THEME hasUserInterfaceStyleChanged = \(hasUserInterfaceStyleChanged ? "YES" : "NO")")  

    if hasUserInterfaceStyleChanged {  
        let userInterfaceStyle = currentTraitCollection.userInterfaceStyle // Either .unspecified, .light, or .dark  

        switch userInterfaceStyle {  
            case .unspecified:  
                print("THEME UIUserInterfaceStyleUnspecified")  
            case .light:  
                print("THEME UIUserInterfaceStyleLight")  
            case .dark:  
                print("THEME UIUserInterfaceStyleDark")  
            }  
    } else {  
        print("THEME NOT CHANGED")  
    }  

}  

Вот зарегистрированные операторы в консоли

Когда появляется новая сцена ...

THEME instance: <MainControllerViewController: 0x117e55910>  
THEME hasUserInterfaceStyleChanged = YES  
THEME UIUserInterfaceStyleLight  

Когда добавленная сцена исчезнет ...

THEME instance: <MainControllerViewController: 0x117e55910>  
THEME hasUserInterfaceStyleChanged = YES  
THEME UIUserInterfaceStyleDark  
THEME instance: <MainControllerViewController: 0x117e55910>  
THEME hasUserInterfaceStyleChanged = YES  
THEME UIUserInterfaceStyleLight  
THEME instance: <MainControllerViewController: 0x117e55910>  
THEME hasUserInterfaceStyleChanged = NO  
THEME NOT CHANGED  
THEME instance: <MainControllerViewController: 0x117e55910>  
THEME hasUserInterfaceStyleChanged = YES  
THEME UIUserInterfaceStyleDark  
THEME instance: <MainControllerViewController: 0x117e55910>  
THEME hasUserInterfaceStyleChanged = YES  
THEME UIUserInterfaceStyleLight  

тем временем я не переходил в темный режим (всегда светлый) ... поэтому я ожидаю, что ТЕМА НЕ ИЗМЕНИЛАСЬ.


person Fabiosoft    schedule 18.09.2019    source источник
comment
См. Раздел stackoverflow.com/questions для возможных причин из-за стольких очевидных изменений черт.   -  person rmaddy    schedule 19.09.2019
comment
Похоже, что когда сцена закрывается, iPadOS внутренне хочет захватить экраны в обоих стилях userInterfaceStyles, поэтому множественные вызовы traitCollectionDidChange (). Обычно это происходит довольно быстро, и вы не должны этого видеть (а тем более замечать). Просто будьте осторожны с тем, что вы делаете на самом деле при изменении userInterfaceStyle. Неправильные задания могут привести к мерцанию светлого и темного режима. Я страдал от этой проблемы, потому что использовал шаблон уведомлений для обновления нескольких viewController. Проблема была решена более прямым подходом.   -  person Phantom59    schedule 24.11.2019
comment
да, я уже решил, но вот в чем причина!   -  person Fabiosoft    schedule 25.11.2019


Ответы (1)


Я боролся с той же проблемой, и решение, которое я разработал, находится в SceneDelegate.

UIScene имеет несколько состояний:

.foregroundActive
.foregroundInactive
.background
.unattached

Когда вы изменяете размер окон в режиме скольжения или, в данном случае, при удалении одного из окон скольжения, для каждого из них вызывается traitCollectionDidChange. Это означает, что вы обновляете userInterfaceStyle для сцен в состояниях .background, .foregroundInactive и .unattached. Это то, что вызывает мерцание.

Решение состоит в том, чтобы не использовать traitCollectionDidChange, а использовать метод делегата в SceneDelegate, называемый windowScene(_:didUpdate:interfaceOrientation:traitCollection:).

Согласно документации Apple этот метод:

Уведомляет вас об изменении размера, ориентации или характеристик сцены.

Дополнительным преимуществом этого является то, что мы можем проверить .activationState сцены перед обновлением userInterfaceStyle.

    func windowScene(_ windowScene: UIWindowScene, didUpdate previousCoordinateSpace: UICoordinateSpace, interfaceOrientation previousInterfaceOrientation: UIInterfaceOrientation, traitCollection previousTraitCollection: UITraitCollection) {

          let currentTraitCollection = windowScene.traitCollection

          if windowScene.activationState == .foregroundActive {
             if currentTraitCollection.userInterfaceStyle != previousTraitCollection.userInterfaceStyle {
                 if currentTraitCollection.userInterfaceStyle == .light {
                     //update to light theme
                 } else {
                     //update to dark theme
                 }
             }
         }
     }
person Shawn Hickman    schedule 15.05.2020