This package gives you the ability to quickly and easily implement theme designs in your SwiftUI application.
Assuming you have a defined theme protocol...
public protocol Theme {
var TextAccent: Color { get }
var TextPrimary: Color { get }
var BodyFont: Font { get }
var CardRadius: CGFloat { get }
}
and have an implementation of that theme...
class BrandThemeLightMode : Theme {
let TextAccent = .red
let TextPrimary = .blue
let BodyFont = .system(size: 12)
let CardRadius = 12.0
}
Add a ThemeConfig.json
file to the root of your target that identifies the protocol you'd like to use.
{
"themeType": "Theme",
"defaultMode": "BrandThemeLightMode"
}
Then, you can simply provide your theme to the ThemeManager
and have it handle the rest!
struct RootView: View {
@StateObject var manager = ThemeManager(.static(BrandThemeLightMode()))
var body: some View {
ContentView()
.themeManaging(self.manager)
}
}
struct ContentView: View {
var body: some View {
VStack {
Text("Hello World")
.foregroundColor(alias: \.TextColor)
.font(alias: \.BodyFont)
}
.cornerRadius(alias: \.CardRadius)
}
}
You can change themes easily during the runtime of your application by simply setting the theme
property of the ThemeManager
. If you would like to have your theme follow the ColorSheme
of the application, simply provide a .dynamic
theme to the manager and it will automatically update to the specified theme as the system updates.
// Instantiation
@StateObject var manager = ThemeManager(.dynamic(light: BrandThemeLightMode(), dark: BrandThemeDarkMode()))
// Or, update the existing manager with a new theme
self.manager.theme = .dynamic(light: BrandThemeLightMode(), dark: BrandThemeDarkMode())
There are some situations where you might want a specific component to never change themes. In this situation, you can simply provide a theme object to the environment on your component and this will override the theme provided by ThemeManager
.
Text("Hello World")
.foregroundColor(alias: \.TextAccent)
// theme override
.environment(\.theme, BrandThemeLightMode())
If you come across a situation where a modifier is not available for your intended purpose, you can still use your theme and alias tokens by gaining access to the current theme object through the use of the ThemeReader
and ThemeAliasReader
respecitively.
ThemeReader { theme in
Text("Hello World")
.foregroundColor(theme.TextAccent)
}
ThemeAliasReader(\.TextAccent) { color in
Text("Hello World")
.foregroundColor(color)
}
Additionally...
There is also a currentTheme
environment property wrapper available to you in situations where you'd prefer to not use one of the aforementioned theme readers.
Note
Use of this property wrapper prevents the ability for you to override the theme provided by the ThemeManger
in this view with the .environment
view modifier in a parent view.
struct ContentView: View {
@Environment(\.currentTheme) private var theme
var body: some View {
Text("Hello World")
.foregroundColor(self.theme.TextAccent)
}
}