in SwiftUI

How to Globally Set a Tint or Accent Color in SwiftUI

TL;DR To make sure all child views inherit the tint/accent color, including those on modal windows, set the TintColor on the root window in SceneDelegate:

//In SceneDelegate.swift

let contentView = ContentView()

        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView)
            self.window = window

            //set the tintColor to be applied globally
            self.window?.tintColor = UIColor.red 
            window.makeKeyAndVisible()
        }

About

In SwiftUI, tint colors are called accent colors, and you can set them on individual views and controls like this:

Button("Hello", action: {}).accentColor(.green)

As with tintColor, accentColor is inherited by child views from their parent. and to set them globally for the entire app, we go to SceneDelegate and set apply an .accentColor() property to our root view.

Problem with global accentColor

To apply an accentColor globally so that it is inherited by all views, one recommendation I’ve come across is to set the accentColor property on the root SwiftUI view inside SceneDelagate, as shown here.

//In SceneDelegate.swift

let contentView = ContentView().accentColor(.red)

The problem I came across when doing this way is that any view opened as a modal sheet will not inherit the accent color, as we can see below.

In this example, the same view with the Button is loaded both by a regular NavigationLink as well as modally through .sheet(). When opened modally, the Button does not inherit the red color, instead it remains the default blue.

Use tintColor Instead

The solution I came across is instead of applying .accentColor to the root SwiftUI view (contentView in our example), we can use the regular old .tintColor property on the root view controller like this:

//In SceneDelegate.swift

        let contentView = ContentView()

        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView)
            self.window = window

            //Set our tintColor her to the root window controller
            self.window?.tintColor = UIColor.red

            window.makeKeyAndVisible()

Now, when we open the new window modally, we will see the Button showing our new red tint color

Below is a full example of the code I used for testing. Note how the same View SubView is being loaded either through NavigationLink or as a sheet.

//ContentView.swift

//Note: Set the .tintColor in SceneDelegate using the example above to change the accent / tint colors

import SwiftUI

struct ContentView: View {
    @State var showSheet = false
    
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink("Navigation Link", destination: SubView())
                    .padding()
                
                Button("Open Modal", action: {
                    self.showSheet.toggle()
                })
            }
            .navigationBarItems(
                leading: Button("Nav Item",
                                action: {}))
                .sheet(isPresented: self.$showSheet, content: {
                    SubView()
                })
        }
    }
}

struct SubView: View {
    var body: some View {
        VStack {
            Text("This window should inherit the tint/accent color")
                .padding()
            Button("Button", action:{})
        }
    }
}