Search code examples
iosswiftswiftuiios-darkmode

Implement dark mode switch in SwiftUI App


I'm currently looking into Dark Mode in my App. While Dark Mode itself isn't much of a struggle because of my SwiftUI basis i'm struggling with the option to set the ColorScheme independent of the system ColorScheme.

I found this in apples human interface guidelines and i'd like to implement this feature. (Link: Human Interface Guidelines)

Any idea how to do this in SwiftUI? I found some hints towards @Environment but no further information on this topic. (Link: Last paragraph)


Solution

  • Single View

    To change the color scheme of a single view (Could be the main ContentView of the app), you can use the following modifier:

    .preferredColorScheme(.dark) // or `.light or` `nil` to use the current scheme
    

    or

    .environment(\.colorScheme, .light) // or `.dark`
    

    Entire App (SwiftUI)

    You can apply it to the ContentView inside the root WindowGroup to make your entire app dark!

    Assuming you didn't change the ContentView name in scene delegate or @main



    Entire App (Including the UIKit parts and The SwiftUI)

    First you need to access the window to change the app colorScheme that called UserInterfaceStyle in UIKit.

    I used this in SceneDelegate (How to add scene delegate in SwiftUI):

    private(set) static var shared: SceneDelegate?
    
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        Self.shared = self
        ...
    }
    

    Then you need to bind an action to the toggle. So you need a model for it.

    struct ToggleModel {
        var isDark: Bool = true {
            didSet { 
                SceneDelegate.shared?.window!.overrideUserInterfaceStyle = isDark ? .dark : .light 
            }
        }
    }
    

    At last, you just need to toggle the switch:

    struct ContentView: View {
         @State var model = ToggleModel()
    
         var body: some View {
             Toggle(isOn: $model.isDark) {
                 Text("is Dark")
            }
        }
    }
    

    From the UIKit part of the app

    Each UIView has access to the window, So you can use it to set the . overrideUserInterfaceStyle value to any scheme you need.

    myView.window?.overrideUserInterfaceStyle = .dark // or `.light` or `.unspecified` to use the current scheme