ShapeStyle is the protocol behind everything you can fill or stroke a shape with, and it
also feeds background and overlay. Knowing its implementations turns one API into colours,
gradients, semantic styles, materials, and image fills.
Concrete Styles
Color and the gradient types are the everyday cases:
ZStack {
Circle()
.fill(Color.yellow)
Circle()
.stroke(Color.green, lineWidth: 16)
}
Circle()
.fill(
LinearGradient(colors: [.green, .yellow], startPoint: .top, endPoint: .bottom)
)
Material conforms too, so Circle().fill(Material.ultraThin) gives a shape a translucent
blur. ImagePaint fills a shape by tiling a region of an image:
Circle().fill(ImagePaint(image: Image(systemName: "star"))).
Semantic Styles
These adapt to context and theme instead of naming a colour:
- Hierarchical styles
.primary,.secondary,.tertiary,.quaternarymap to the numbered content levels:Circle().fill(.quaternary). BackgroundStyle()andForegroundStyle()resolve to the context's background and foreground, correct in both light and dark mode.SelectionShapeStyle()draws the platform's selection highlight.TintShapeStyle()uses the tint colour, which defaults to the app accent and follows atintmodifier up the hierarchy:
Circle()
.fill(TintShapeStyle())
.tint(Color.yellow)
Prefer these semantic styles over hard-coded colours when the surface should track the theme.
AnyShapeStyle
AnyShapeStyle type-erases a style, so a view can accept any style as a stored property without
becoming generic.
struct AvatarView: View {
let style: AnyShapeStyle
var body: some View {
Circle()
.fill(style)
}
}
let avatarView = AvatarView(style: AnyShapeStyle(TintShapeStyle()))