iosswiftxcodeuikituiscene

Dark mode not enabling/disabling when using window.overrideUserInterfaceStyle on iPad


I have successfully implemented the dark mode toggle by calling window.overrideUserInterfaceStyle in my app. When running the app in real iPhone or simulator, the function works fine.

But when I run the same script in a iPad, only the view.overrideUserInterfaceStyle works. If I try to use window.overrideUserInterfaceStyle it does not update the trait collection (and neither call traitCollectionDidChange(_).

To change the style I am doing (style from white to dark):

UIApplication.shared.connectedScenes.forEach { (scene: UIScene) in
    (scene.delegate as? SceneDelegate)?.window?.overrideUserInterfaceStyle = .dark //Just this one works on iPhone.
}
UIApplication.shared.delegate?.window??.overrideUserInterfaceStyle = .dark //Force
UIWindow.appearance().overrideUserInterfaceStyle = .dark //Force

When the code is executed above, it should replace the color of all UIViews that are configured with UIColor for light and dark styles. Also call traitCollectionDidChange(_). But none of these actions are happening on the iPad simulator.

This code above only works on the iPhone real/simulator, it should work on the iPad simulator too. I don't have any iPad capable of dark style here.

Maybe it's a bug on the simulator?

I also tried to create a sample app and the style change works on iPad indeed, but since it's a clean project with no libraries it should work.

I am also trying to minimize my app, still not working so I am afraid if it's a library creating a conflict.

The problem also happens when using UIApplication setup instead of UIScene.

I am using Xcode 11.3 and Swift 5 with some Cocoapod libraries.


Solution

  • If you are using custom transition, I recommend you to set viewController.overrideUserInterfaceStyle = .unspecified after you finish the animation.

    Like this:

    if #available(iOS 13.0, *) {
        viewController.overrideUserInterfaceStyle = .dark
    }
    
    UIView.animate(withDuration: defaultAnimationDuration, delay: 0, animations: {
        //animate viewController
    }, completion: { [weak self] _ in
        if #available(iOS 13.0, *) {
            viewController.overrideUserInterfaceStyle = .unspecified
        }
    })
    

    This will make your UIViewController follow the UIWindow user interface style after all.