6

What is the best way to change app tint color in a SwiftUI app?

It is powered by the new SwiftUI lifecycle so I do not have the option to perform self.?tintColor

Tried searching here but didn't find any way to do it in a SwiftUI lifecycle app.

3 Answers3

8

This works without an EnvironmentKey, and propagates to all views in the app:

@main

struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .accentColor(.red) // pick your own color
        }
    }
}
koen
  • 5,383
  • 7
  • 50
  • 89
  • 1
    I find this to be the correct answer. The answer above by Itay may work but it's really not utilizing SwiftUI. – Justin Ngan Oct 23 '22 at 18:11
5

In the SceneDelegate.swift where you create the window for your app you can set the tint color globally using the tintColor property of UIWindow

let contentView = ContentView()

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

    self.window?.tintColor = UIColor.red // Or any other color you want
    window.makeKeyAndVisible()
}

Edit
After seeing that you want it for the new SwiftUI, you can create new EnvironmentKeys:

private struct TintKey: EnvironmentKey {
    static let defaultValue: Color = Color.blue
}

extension EnvironmentValues {
    var tintColor: Color {
        get { self[TintKey.self] }
        set { self[TintKey.self] = newValue }
    }
}
   
@main
struct YourApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView().environment(\.tintColor, Color.red)
        }
    }
}

Then in your views you would use it like this:

struct ContentView: View {
    @Environment(\.tintColor) var tintColor
    
    var body: some View {
        VStack {
            Text("Hello, world!")
                .padding()
            Button(action: {}, label: {
                Text("Button")
            })
        }.accentColor(tintColor)       
    }
}
Itay Brenner
  • 800
  • 6
  • 19
0

Ariel, In this implementation, you can choose and use 'accentColor' throughout the entire application lifecycle. However, if you want to keep a value permanently, you should think about a solution, I hope you are a smart guy ...

 enum MyColor: Identifiable, CaseIterable {
    var id: String { UUID().uuidString }
    case blue, green, orange, pink, purple, red, yellow
    var currentColor: Color {
        switch self {
        case .blue:
            return .blue
        case .green:
            return .green
        case .orange:
            return .orange
        case .pink:
            return .pink
        case .purple:
            return .purple
        case .red:
            return .red
        case .yellow:
            return .yellow
        }
    }
}

final class ViewModel: ObservableObject {
    @Published
    var isPresented = false
    @Published
    var myColor: MyColor = .blue
}

struct AppSettings: View {
    @ObservedObject
    var vm: ViewModel
    var body: some View {
        NavigationView {
            Form {
                Picker("Current color", selection: $vm.myColor) {
                    ForEach(MyColor.allCases) { color in
                        Label(
                            title: {
                                Text(color.currentColor.description.capitalized)
                            }, icon: {
                                Image(systemName: "circle.fill")
                            })
                            .tag(color)
                            .foregroundColor(color.currentColor)
                    }
                }
            }
            .navigationBarItems(
                trailing:
                    Button(
                        action: {
                            vm.isPresented.toggle()},
                        label: {
                Text("Close")
            }))
            .navigationTitle("App settings")
        }
    }
}

struct ContentView: View {
    @StateObject
    private var vm = ViewModel()
    var body: some View {
        VStack {
            Button(
                action: {
                    vm.isPresented.toggle()
                }, label: {
                    VStack {
                        Rectangle()
                            .fill(Color.accentColor)
                            .frame(width: 100, height: 100)
                        Text("Settings")
                            .font(.title)
                    }
                })
        }
        .accentColor(vm.myColor.currentColor)
        .sheet(
            isPresented: $vm.isPresented) {
            AppSettings(vm: vm)
                .accentColor(vm.myColor.currentColor)
        }
    }
}
Dim Novo
  • 367
  • 3
  • 8