The StylesheetResolvable protocol allows you to decode SwiftUI primitives (like HorizontalAlignment or ShapeStyle) that need to be resolved from a stylesheet.
Many SwiftUI types have a nested .Resolvable type. You can use these in your modifiers to allow dynamic values in stylesheets.
Examples:
1. Using built-in SwiftUI types (e.g., HorizontalAlignment):
struct MyModifier<Root: RootRegistry>: ViewModifier, Decodable {
let alignment: HorizontalAlignment.Resolvable
func body(content: Content) -> some View {
VStack(alignment: alignment.resolve(on: element, in: context)) { content }
}
}
Stylesheet: myModifier(alignment: .trailing)
2. Using specialized protocols (e.g., ShapeStyle):
Use StylesheetResolvableShapeStyle to decode a type-erased ShapeStyle.
struct FillBackgroundModifier<Root: RootRegistry>: ViewModifier, @preconcurrency Decodable {
let fill: StylesheetResolvableShapeStyle
init(_ fill: StylesheetResolvableShapeStyle) {
self.fill = fill
}
func body(content: Content) -> some View {
content.background(fill)
}
}
Stylesheet: fillBackground(.red.opacity(attr("opacity")))
3. Creating custom resolvable types:
Conform your own struct to StylesheetResolvable to allow its properties to use attr(<name>).
struct Video {
let url: String
let resolution: Int
struct Resolvable: StylesheetResolvable, Decodable {
let url: AttributeReference<String>
let resolution: AttributeReference<Int>
func resolve(on element: ElementNode, in context: LiveContext<some RootRegistry>) -> Video {
Video(
url: url.resolve(on: element, in: context),
resolution: resolution.resolve(on: element, in: context)
)
}
}
}
Stylesheet: backgroundVideo(Video("...", in: attr("resolution")))
@ASTDecodable("fillBackground")
struct FillBackgroundModifier<Root: RootRegistry>: ViewModifier, @preconcurrency Decodable {
let fill: StylesheetResolvableShapeStyle
init(_ fill: StylesheetResolvableShapeStyle) {
self.fill = fill
}
func body(content: Content) -> some View {
content.background(fill)
}
}