let theme: Theme?
@Environment(\.appTheme) private var envTheme
func body(content: Content) -> some View {
guard let theme = theme ?? (envTheme as? Theme) else {...}
let font = theme.text.font
let textColor = theme.text.primaryColor
return AnyView(
content
.font(font)
.foregroundColor(textColor)
)
}
}
extension View {
func themed(_ theme: Theme? = nil) -> some View {
modifier(ThemedModifier(theme: theme))
}
}
struct ContentView: View {
@Environment(\.appTheme) private var theme
var body: some View {
// ERROR: Type 'any ThemeProtocol' cannot conform to 'ThemeProtocol'
Text("Hello, Modifier external!")
.themed(theme)
}
}
Вопрос
Что мне нужно изменить, чтобы модификатор мог принять либо явную тему, либо из окружающей среды? Ошибки?// MARK: - Tokens
protocol ThemeTextTokens {
var primaryColor: Color { get }
var font: Font { get }
}
protocol ThemeButtonTokens {
var neutralColor: Color { get }
var positivColor: Color { get }
var accentColor: Color { get }
}
// MARK: - Theme Protocol
protocol ThemeProtocol {
associatedtype TextTokens: ThemeTextTokens
associatedtype ButtonTokens: ThemeButtonTokens
var text: TextTokens { get }
var button: ButtonTokens { get }
}
// MARK: - Concrete Theme
public struct MyTheme: ThemeProtocol {
struct TextTokens: ThemeTextTokens {
var primaryColor: Color = .primary
var font: Font = .title
// Extra property not in the protocol
var accentColor: Color = .accentColor
}
struct ButtonTokens: ThemeButtonTokens {
var neutralColor: Color = .primary
var positivColor: Color = .green
var accentColor: Color = .accentColor
}
var text = TextTokens()
var button = ButtonTokens()
}
// MARK: - Environment
private struct ThemeEnvKey: EnvironmentKey {
static var defaultValue: (any ThemeProtocol)? = nil
}
extension EnvironmentValues {
var appTheme: (any ThemeProtocol)? {
get { self[ThemeEnvKey.self] }
set { self[ThemeEnvKey.self] = newValue }
}
}
// MARK: - ViewModifier
struct ThemedModifier: ViewModifier {
let theme: Theme?
@Environment(\.appTheme) private var envTheme
func body(content: Content) -> some View {
guard let theme = theme ?? (envTheme as? Theme) else {
return AnyView(content)
}
let font = theme.text.font
let textColor = theme.text.primaryColor
return AnyView(
content
.font(font)
.foregroundColor(textColor)
)
}
}
extension View {
func themed(_ theme: Theme? = nil) -> some View {
modifier(ThemedModifier(theme: theme))
}
}
// MARK: - Demo
struct ContainerView: View {
var body: some View {
ContentView()
.environment(\.appTheme, MyTheme())
}
}
struct ContentView: View {
@Environment(\.appTheme) private var theme
var body: some View {
VStack(spacing: 12) {
// direct access
Text("Hello, World!")
.foregroundColor(theme?.text.primaryColor ?? .black)
// downcast to concrete type works
Text("Hello, Accent!")
.foregroundColor((theme as? MyTheme)?.text.accentColor ?? .black)
//
// "Type 'any ThemeProtocol' cannot conform to 'ThemeProtocol'"
Text("Hello, Modifier external!")
.themed(theme)
// OK
Text("Hello, Modifier internal!")
.themed(theme)
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/797 ... nvironment
Мобильная версия