LabeledContent (iOS 16, WWDC22) pairs a label with content, label leading and content trailing. Unlike a hand-rolled HStack with a Spacer, it adapts to its parent container and applies platform styling, such as secondary color on the content.
Basic Rows in a Form
The string version is one line. Any value with a FormatStyle works too, so numbers and dates need no manual formatting.
Form {
LabeledContent("Label", value: "Content")
LabeledContent(
"Number",
value: 100.0,
format: .number.precision(.fractionLength(0))
)
}
Both label and content can be arbitrary views, which is how a control gets a proper label inside a Form.
Form {
LabeledContent {
Stepper(value: $value, in: 0...10.0) {
Text(value.formatted(.number))
}
} label: {
Text("Count")
}
}
Styling With LabeledContentStyle
Conform to LabeledContentStyle and implement makeBody. Wrap the passed Configuration in a new LabeledContent to keep the standard layout and only restyle it.
struct AccentedLabeledContentStyle: LabeledContentStyle {
func makeBody(configuration: Configuration) -> some View {
LabeledContent(configuration)
.foregroundColor(.accentColor)
}
}
extension LabeledContentStyle where Self == AccentedLabeledContentStyle {
static var accented: AccentedLabeledContentStyle { .init() }
}
LabeledContent("Label", value: "Content")
.labeledContentStyle(.accented)
For a different layout, compose configuration.label and configuration.content yourself instead.
struct VerticalLabeledContentStyle: LabeledContentStyle {
func makeBody(configuration: Configuration) -> some View {
VStack(alignment: .leading) {
configuration.label
configuration.content
}
}
}
labeledContentStyle travels through the environment, so one modifier styles a whole hierarchy until a child overrides it. labelsHidden() hides the label while keeping it for accessibility.