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 Twitter.