Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ForEach inside ScrollView doesn't take whole width

Tags:

I'm trying to re-create UI of my current app using SwiftUI. And it is way more difficult than I initially though. I wanted to achieve card-like cells with some background behind them. I found that List doesn't support that, at least yet. List is so limited - it doesn't allow you to remove cell separator.

So I moved to ForEach inside ScrollView. I guess that isn't something which should be used in production for long tables but that should work for now. The problem I have is that ForeEach view doesn't take all the width ScrollView provides. I can set .frame(...) modifier but that will require hardcoding width which I definitely don't want to do.

Any ideas how to force VStack take full width of the ScrollView? I tried to use ForeEach without VStack and it has the same issue. It seems like ScrollView (parent view) "tells" its child view (VStack) that its frame is less that actual ScrollView's frame. And based on that information child views build their layout and sizes.

Here is my current result:

And here is the code:

struct LandmarkList : View {
    var body: some View {
        NavigationView {
            ScrollView() {
                VStack {
                    Spacer().frame(height: 160)
                    ForEach(landmarkData) { landmark in
                        LandmarkRow(landmark: landmark).padding([.leading, .trailing], 16)
                    }
                }.scaledToFill()
                .background(Color.pink)
            }
            .background(Color.yellow)
            .edgesIgnoringSafeArea(.all)
            .navigationBarTitle(Text("Landmarks"))

        }
    }
}

struct LandmarkRow : View {
    var landmark: Landmark

    var body: some View {
        HStack {
            VStack(alignment: .leading) {
                Text(landmark.name).font(.title)
                Text("Subtitle")
                    .font(.callout)
                    .color(.gray)
            }
            Spacer()
            Text("5 mi")
                .font(.largeTitle)
            }.frame(height: 80)
            .padding()
            .background(Color.white)
            .cornerRadius(16)
            .clipped()
            .shadow(radius: 2)
    }
}
like image 723
OgreSwamp Avatar asked Jun 10 '19 23:06

OgreSwamp


1 Answers

I've got the same issue, the only way I have found so far is to fix the ScrollView and the content view width, so that every subview you add inside the content view will be centered.

I created a simple wrapper that take the width as init parameter

struct CenteredList<Data: RandomAccessCollection, Content: View>: View where Data.Element: Identifiable {

    public private(set) var width: Length
    private var data: Data
    private var contentBuilder: (Data.Element.IdentifiedValue) -> Content

    init(
        width: Length = UIScreen.main.bounds.width,
        data: Data,
        @ViewBuilder content: @escaping (Data.Element.IdentifiedValue) -> Content)
    {
        self.width = width
        self.data = data
        self.contentBuilder = content
    }

    var body: some View {
        ScrollView {
            VStack {
                ForEach(data) { item in
                    return self.contentBuilder(item)
                }.frame(width: width)
            }
            .frame(width: width)
        }
        .frame(width: width)
    }
}

By default it takes the screen width (UIScreen.main.bounds.width).

It works just like a List view:

var body: some View {
    TileList(data: 0...3) { index in
        HStack {
            Text("Hello world")
            Text("#\(index)")
         }
    }
}
like image 158
rraphael Avatar answered Nov 15 '22 05:11

rraphael