Customize List in SwiftUI
• 6 min read
swiftui
Customizing the appearance and behavior of Lists in SwiftUI is straightforward, though it can depend on the desired effect and the platform
Changing Row Content
The most common form of customization is providing custom content for each row, often using a combination of views like HStack, VStack, Text, and Image.
struct CustomRowView: View {
let item: String
var body: some View {
HStack {
Image(systemName: "star.fill") // Custom icon
.foregroundColor(.yellow)
VStack(alignment: .leading) {
Text(item) // Main text
.font(.headline)
Text("Detail for \(item)") // Detail text
.font(.subheadline)
.foregroundColor(.gray)
}
Spacer() // Pushes content to the left
Text("New")
.padding(5)
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(5)
}
}
}
struct ContentView: View {
let data = ["Apple", "Banana", "Cherry", "Date"]
var body: some View {
List {
// Using the custom row view for each item
ForEach(data, id: \.self) { item in
CustomRowView(item: item)
}
}
}
}
Adjusting Separators and Row Insets (iOS)
On iOS, you can hide the default row separators and manage insets.
- Hiding Separators: You can apply the modifier listRowSeparator(.hidden) to the individual row content.
List {
ForEach(data, id: \.self) { item in
Text(item)
.listRowSeparator(.hidden) // Hides the separator for this row
}
}
// This style is often paired with setting the list style to .plain or .insetGrouped
- Managing Insets: Use listRowInsets() to change the padding around the content of a row.
Text("A Wider Row")
.listRowInsets(EdgeInsets(top: 10, leading: 50, bottom: 10, trailing: 50))
Changing the List Style
SwiftUI provides several built-in list styles that dramatically change the overall appearance, especially on iOS. Apply the .listStyle() modifier to the List.
| Style | Appearance/Behavior | When to Use |
|---|---|---|
| .automatic | Default for the platform. | General-purpose. |
| .plain | Rows extend edge-to-edge; headers/footers stick (iOS 15+). | Simple, linear lists. |
| .grouped | Sections appear in distinct, rounded blocks. | Settings, form-like data. |
| .insetGrouped | Similar to grouped, but inset from the screen edges. | iOS standard for settings screens. |
| .sidebar | Designed for navigation/split views (iOS/iPadOS/macOS). | Primary navigation lists. |
List {
Section("Fruit") {
Text("Apple")
}
}
.listStyle(.insetGrouped) // Applies the iOS "Settings" style
Swipes Actions and Context Menus
- Swipes Actions (Trailing/Leading)
You can add custom actions that appear when the user swipes a row (iOS 15+).
ForEach(data, id: \.self) { item in
Text(item)
.swipeActions(edge: .trailing) { // Define actions for swiping right-to-left
Button("Archive") {
// Archive logic
}
.tint(.orange)
Button("Delete") {
// Delete logic
}
.tint(.red)
}
.swipeActions(edge: .leading) { // Optional: for swiping left-to-right
Button("Pin") {
// Pin logic
}
.tint(.blue)
}
}
- Context Menu (Long-Press)
Use the .contextMenu modifier to show a menu when a row is long-pressed (or right-clicked).
Text("Tap and Hold Me")
.contextMenu {
Button("Edit") { /* ... */ }
Button("Share") { /* ... */ }
Divider()
Button("Delete", role: .destructive) { /* ... */ }
}