How to add SegmentedControl in List that scrolls with a List in SwiftUI?

January 13, 2025

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:

The right way: Using a SegmentedControl in the List header

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:

Removing extra space

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.