SwiftUI app theme switch
This short article will explain how to change your app theme when app is running and all the needed stuff to make it work at runtime.
So first things first, lets create our Xcode project and assets that we will use on one simple view that will demonstrate switch between dark/light theme and also implement the check if system is using dark/light theme.
Open Xcode and create new project like File-> New -> Project. Choose iOS as template.
Next, create your identifier and choose SwiftUI as interface and Swift as language.
After we have created our project lets organise views into folders and add some colour assets that will distinguish our light from dark mode.
So first create folder Views in root of our project and move ContentView.swift to it and create new HomeView.swift inside same folder.
Next lets create our colour Assets. Click on Assets and on navigation bar Right click -> New Colour Set.
Lets name our new colour set “Accent”. Lets add three more colour sets and name them:
- BackgroundColor
- BackgroundColorList
- ColorPrimary
- TextColorPrimary
- TextColorSecondary
After we have created our set of colours we must create Extension to use our custom colours and modifiers. Create folder Extensions and add Color.swift extension.
extension Color {
static var theme: Color {
return Color("theme")
}
static var BackgroundColor: Color {
return Color("BackgroundColor")
}
static var BackgroundColorList: Color {
return Color("BackgroundColorList")
}
static var ColorPrimary: Color {
return Color("ColorPrimary")
}
static var Accent: Color {
return Color("AccentColor")
}
static var TextColorPrimary: Color {
return Color("TextColorPrimary")
}
static var TextColorSecondary: Color {
return Color("TextColorSecondary")
}
}
Next lets create new folder called Fonts and copy/paste our fancy fonts to new folder.
Also create new folder called Utils and add Constants.swift class and UserDefaultsUtils.swift class that will be our singleton for setting/getting our theme of app.
Constants.swift
import Foundation
class Constants {
public static let DARK_MODE = “DARK_MODE”
public static let LIGHT_MODE = “LIGHT_MODE”
}
UserDefaultsutils.swift
import Foundation
class UserDefaultsUtils {
static var shared = UserDefaultsUtils()
func setDarkMode(enable: Bool) {
let defaults = UserDefaults.standard
defaults.set(enable, forKey: Constants.DARK_MODE)
}
func getDarkMode() -> Bool {
let defaults = UserDefaults.standard
return defaults.bool(forKey: Constants.DARK_MODE)
}
}
So lets add some code to our HomeView.swift. First we are going to add Environment variable that will represent system colour scheme that is currently used if we decide to use same theme as current system theme. Second we will use state property isDarkModeOn that will keep our theme state of our window, and which will be saved to UserDefaults for later use.
@Environment(\.colorScheme) private var colorScheme: ColorScheme
@State private var isDarkModeOn = false
Next lets add our function for setting theme of our app when we open and function for changing theme on Toggle.
func setAppTheme(){
//MARK: use saved device theme from toggle
isDarkModeOn = UserDefaultsUtils.shared.getDarkMode()
changeDarkMode(state: isDarkModeOn)
//MARK: or use device theme
/*if (colorScheme == .dark)
{
isDarkModeOn = true
}
else{
isDarkModeOn = false
}
changeDarkMode(state: isDarkModeOn)*/
}
func changeDarkMode(state: Bool){
(UIApplication.shared.connectedScenes.first as?
IWindowScene)?.windows.first!.overrideUserInterfaceStyle = state ? .dark : .light
UserDefaultsUtils.shared.setDarkMode(enable: state)
}
And finally our HomeView.swift body.
var ToggleThemeView: some View {
Toggle("Dark Mode", isOn: $isDarkModeOn).onChange(of: isDarkModeOn) { (state) in
changeDarkMode(state: state)
}.labelsHidden()
}
var body: some View {
NavigationView {
ZStack {
Color.BackgroundColorList.edgesIgnoringSafeArea(.all)
VStack(alignment: .leading) {
Text("Switch theme").foregroundColor(Color.TextColorSecondary).padding(10).font(F ont.custom("Baloo-Regular", size: 15))
ToggleThemeView
}
.background(Color.BackgroundColorList)
.font(Font.custom("Baloo-Regular", size: 20))
.navigationBarTitle("", displayMode: .inline)
.navigationBarItems(
leading:
Text("DarkModeSwitch").font(Font.custom("Baloo-Regular", size: 20)))
.navigationBarBackButtonHidden(true)
foregroundColor(Color.TextColorPrimary)
}
}
.background(Color.BackgroundColorList)
.navigationViewStyle(StackNavigationViewStyle())
.onAppear(perform: {
setAppTheme()
})
}
So if we open our app first theme will be light. We can toggle it to dark, or use default device theme.
That is it. Code is available on GitHub repository.
GitHub