Styling SwiftUI Form
Reading time: 3 min
This recipe shows how to style a SwiftUI Form. Forms are a great way to quickly compose a UI for collecting data, such as an enrolment form or a settings panel, but fully styling them can be a bit a tricky.
The end result will look like this:

Starting position
OK, let's start with a an exemplary Form that contains most components you'd normally find in one:
struct FormTest: View {
@State private var textFieldSelection = ""
@State private var pickerSelection = "One"
@State private var isToggleOn = false
@State private var datePickerSelection = Date()
var body: some View {
NavigationView {
Form {
TextField("TextField", text: $textFieldSelection)
Text("Some text")
Button("Button") { }
Section(header: Text("Header"), footer: Text("Footer")) {
Text("Section text")
}
Picker(selection: $pickerSelection, label: Text("Picker")) {
ForEach(["One", "Two", "Three"], id: \.self) {
Text($0).tag($0)
}
}
Toggle(isOn: $isToggleOn) {
Text("Toggle")
}
NavigationLink("Navigation Link", destination: Text("Destination"))
Picker(selection: $pickerSelection, label: Text("Picker")) {
ForEach(["One", "Two", "Three"], id: \.self) {
Text($0).tag($0)
}
}.pickerStyle(SegmentedPickerStyle())
DatePicker("Date picker", selection: $datePickerSelection)
}.navigationBarTitle("Form Styling", displayMode: .inline)
}
}
}
It looks like this:

Changing foreground / text color
This one's simple enough - the foregroundColor modifier works as you'd expect:
Form {
// ...
}.navigationBarTitle("Form Styling", displayMode: .inline)
.foregroundColor(.red) // ADD THIS

Note that it doesn't affect a Picker with SegmentedPickerStyle, nor the button text of DatePicker.
Effect of accentColor
The accentColor modifier applies the accent to controls that support it, including the DatePicker buttons:
Form {
// ...
}.navigationBarTitle("Form Styling", displayMode: .inline)
.foregroundColor(.red)
.accentColor(.orange) // ADD THIS

Changing the background color
This is the tricky one. If you just apply the background modifier to your Form, nothing happens:
Form {
// ...
}.navigationBarTitle("Form Styling", displayMode: .inline)
.foregroundColor(.red)
.accentColor(.orange)
.background(Color.green) // ADD THIS, BUT TO NO EFFECT
This is because Form is internally represented by a grouped UITableView and its background color renders on top of yours. The solution is to set the default UITableView background color to UIColor.clear. Since this is a global-scope change, we'll be smart about it and apply it in onAppear, only to revert it back in onDisappear:
.foregroundColor(.red)
.accentColor(.orange)
.background(Color.green)
.onAppear { // ADD THESE
UITableView.appearance().backgroundColor = .clear
}
.onDisappear {
UITableView.appearance().backgroundColor = .systemGroupedBackground
}
This leaves us with the following result:

As you can see, the background of the Form itself did change, but its rows are still white. To color them, you must use the listRowBackground modifier. You can apply it to individual elements like this:
TextField("TextField", text: $textFieldSelection)
.listRowBackground(Color.blue)
Or to individual sections:
Section(header: Text("Header"), footer: Text("Footer")) {
Text("Section text")
}.listRowBackground(Color.blue)
Or, if you want to apply it to every row in the Form, wrap everything in a Group and apply the modifier to it:
Form {
Group {
// all the controls go here, just as before
}.listRowBackground(Color(.sRGB, red: 0, green: 1, blue: 1, opacity: 0.5))
}
This results in most of the Form fully styled:

A note about Picker and DatePicker
If you wrap your Form in a NavigationView, its Picker default style will render a navigation link that takes the user to a list of picker items. Note that out of all the form styling that we just did, only foregroundColor affects the picker screen:

On the other hand, DatePicker popups are only affected by accentColor.