When building apps with SwiftUI, you might need to add a SegmentedControl on top of a List that scrolls along with the list.
Usually, you’d think of placing the List inside a VStack and adding a Picker with the .segmented style above it, like this:
VStack(spacing: .zero) {
Picker("Conact type", selection: $genre) {
ForEach(Genre.allCases, id: \.self) { genre in
Text(genre.string)
.textCase(nil)
}
}
.pickerStyle(.segmented)
.padding(.horizontal)
.background(Color(uiColor: .systemGroupedBackground))
List {
Section {
ForEach(songs, id: \.id) { song in
VStack(alignment: .leading) {
Text(song.title)
.foregroundStyle(.primary)
.bold()
Text(song.artist)
.foregroundStyle(.secondary)
}
}
}
}
}However, this approach doesn’t give the desired result:
Instead of adding the Picker in a VStack, you can include it in the List's section header, like this:
List {
Section {
ForEach(songs, id: \.id) { song in
VStack(alignment: .leading) {
Text(song.title)
.foregroundStyle(.primary)
.bold()
Text(song.artist)
.foregroundStyle(.secondary)
}
}
} header: { Picker("Conact type", selection: $genre) { ForEach(Genre.allCases, id: \.self) { genre in Text(genre.string) .textCase(nil) } } .pickerStyle(.segmented) }}This gives a much better result:
You might notice unwanted spacing around the SegmentedControl. To fix this, apply the .listRowInsets(EdgeInsets()) modifier to the Picker:
List {
Section {
ForEach(songs, id: \.id) { song in
VStack(alignment: .leading) {
Text(song.title)
.foregroundStyle(.primary)
.bold()
Text(song.artist)
.foregroundStyle(.secondary)
}
}
} header: {
Picker("Conact type", selection: $genre) {
ForEach(Genre.allCases, id: \.self) { genre in
Text(genre.string)
.textCase(nil)
}
}
.pickerStyle(.segmented)
.listRowInsets(EdgeInsets()) .padding(.vertical)
}
}Here’s the final output: a SegmentedControl that scrolls seamlessly with the list.
Thanks for reading! If you have any questions or suggestions, feel free to send me DM on X. If you enjoyed this article and would like to support me, Buy me a coffee.