macosswiftuiswiftui-list

SwiftUI MacOS - scrollable List


I might be missing something obvious here, but I can't find how to get around it. I want to have List() with limited height, that allows me to scroll inside it. This is an example code:

struct TestView: View {
    var items: [String] = [
        "Item 1", "Item 2", "Item 3",
        "Item 4", "Item 5", "Item 6",
        "Item 7", "Item 8", "Item 9",
        "Item 10"
    ]
    @State var selected: String = "one"

    var body: some View {
        Form {
            Section(header: Text("A section")) {
                List(selection: $selected) {
                    ForEach(items, id: \.self) { item in
                        Text(item)
                    }
                }
                .frame(height: 100)
            }
        }
        .formStyle(.grouped)
    }
} 

I was expecting the List to allow me to scroll inside the 100pt height, but instead the list is just cut off, without any possibility of scrolling. If I wrap the whole List in a ScrollView, everything disappears.

How to enable scrolling of a List inside explicitly limited space?


Solution

  • I suspect that Lists in a .grouped style Form is not scrollable by design. It's only the Form itself that is scrollable.

    Changing to the .sidebar list style makes the list scrollable:

    .listStyle(.sidebar)
    

    Alternatively, you can embed it in a ScrollView like this:

    ScrollView {
        List(selection: $selected) {
            ForEach(items, id: \.self) { item in
                Text(item)
            }
        }
        .containerRelativeFrame(.vertical)
    }
    .frame(height: 100)
    

    Without .containerRelativeFrame(.vertical), the List will have a very small height, and is effectively invisible. This is because Lists usually takes up all the available height of its container, but in a vertical ScrollView, the "available height" is infinite.

    containerRelativeFrame is here to make sure the height of the List matches the height of the ScrollView.

    Note that this works not directly because of the ScrollView. It's because the List no longer thinks it is in a grouped style Form anymore, and the List becomes scrollable again. The outer ScrollView is actually also scrollable, but since the List height matches the scroll view height exactly, you can't tell just by scrolling. There is actually 3 layers of scrollable views nested here - the form, the ScrollView, and the List.

    Neither of these solutions preserve the style that the List would have gotten, if it were put directly inside the Form. Based on how that style looks, it doesn't seem to be designed to be scrollable.