How to embed List in ScrollView using SwiftUI

Hi! I want to allow user to scroll a long list of items iOS app, so I am trying to embbed a List view in ScrollView view. However, the preview result doesn't work. Can you help me to check what I am missing?


Below is my experimental code I entered to the file ContentView.swift, started with Single Page App project.

struct ContentView: View {
    var body: some View {
        ScrollView{
            List{
                Text("abc")
                Text("def")
            }
            .border(Color.yellow, width: 3)
            .background(Color.blue)
        }.padding(10).border(Color.red, width: 3)
    }
}


The preview of this code shows the red borders of the ScrollView, but not showing anything for the list, nor the text inside. If I change "List" to "VStack", then everything worked as expected.


I don't understand why List doesn't work, and don't know how to fix it. Can you help me to find some clue?


Thank you!

Accepted Reply

List should already scroll—it's equivalent to UITableView. Simply remove the ScrollView and your application should work. For instance this will almost match what you've described above:


List {
    Text("abc")
    Text("def")
}
.border(Color.yellow, width: 3)
.background(Color.blue)
.padding(10)
.border(Color.red, width: 3)


Now, if your aim is to have the red border and the padding remain on screen while the list and its yellow border are scrolling, then you may have to do things a little differently. In this case, you can switch out the List with a ForEach to iterate over a collection, manually using a VStack to place a Divider view beneath each cell:


ScrollView {
    ForEach(names, id: \.self) { name in
        VStack {
            Text(name)
            Divider()
        }
    }
    .border(Color.yellow, width: 3)
    .background(Color.blue)
}
.padding(10)
.border(Color.red, width: 3)


Note that only a List will provide the appearance of 'empty' cells to fill up the screen.

Replies

List should already scroll—it's equivalent to UITableView. Simply remove the ScrollView and your application should work. For instance this will almost match what you've described above:


List {
    Text("abc")
    Text("def")
}
.border(Color.yellow, width: 3)
.background(Color.blue)
.padding(10)
.border(Color.red, width: 3)


Now, if your aim is to have the red border and the padding remain on screen while the list and its yellow border are scrolling, then you may have to do things a little differently. In this case, you can switch out the List with a ForEach to iterate over a collection, manually using a VStack to place a Divider view beneath each cell:


ScrollView {
    ForEach(names, id: \.self) { name in
        VStack {
            Text(name)
            Divider()
        }
    }
    .border(Color.yellow, width: 3)
    .background(Color.blue)
}
.padding(10)
.border(Color.red, width: 3)


Note that only a List will provide the appearance of 'empty' cells to fill up the screen.

What's your purpose in embedding a List instead of a VStack in the scrollView.


This works an scrolls:


struct ContentView: View {
    var body: some View {
        ScrollView {
            VStack {
                Text("abc").frame(width: 200, height: 50)
                Text("def").frame(width: 200, height: 50)
            }
            .border(Color.yellow, width: 3)
            .background(Color.blue)
        }.padding(10).border(Color.red, width: 3)
    }
}



Or simply a List, which automatically scrolls:


struct ContentView: View {
    var body: some View {
        List {
            Text("abc").frame(width: 200, height: 50)
            Text("def").frame(width: 200, height: 50)
        }.padding(10).border(Color.red, width: 3)
    }
}
  • I think having a VStack containing all the elements in a List or ScrollView breaks the UI performance. Having all the elements in VStack will show them all at once (even they are not on the screen). You can try that by simply printing in .onApper function.

Add a Comment

Hi Jim Dovey,


You are right. I didn't realize that List can already let me scroll for the content more than one screen. The ForEach solution works for me because it seems to support more than 10 items inside.

Thank you!


struct ContentView: View {
    var body: some View {
        List {
            ForEach(1 ... 100, id: \.self ) {
                x in
                VStack {
                Text("dfsf")
                Text("sfsdfj")
                }
            }
        }.padding(10)
    }
}

Hi Claude31,


My original purpose is to display a long list of content without realizing List can already scroll. Now the problem is resolved. Thank you!

A list will scroll vertically. However, I don't see a way to get horizontal scrolling or selecting of elements within the list. Does anyone know how to do these things?

Notes has a Recently Deleted folder view that shows a search field and some captions above the list. The whole vertical stack is scrollable. That makes me think they're using UIKit and not SwiftUI.

Video

Using ForEach with a Divider won't allow .swipeActions to work, as it must be inside a List.

  • I've also run into this issue. Trying to use .swipeActions on a List inside a ScrollView. The view has header content which is not part of the list, and if I use try to use a List for the whole view in place of the ScrollView, it works but there doesn't seem to be a way to disable the row style on the header content.

Add a Comment