exyte / Grid

The most powerful Grid container missed in SwiftUI
MIT License
1.76k stars 93 forks source link

Grid disappears when placed inside a ScrollView #6

Closed oalansari82 closed 2 years ago

oalansari82 commented 4 years ago

When the grid is placed inside a scrollview, the grid just disappears.

import SwiftUI
import ExyteGrid

struct ContentView: View {

    var body: some View {

        ScrollView {
            Grid(array, id: \.id, tracks: [.fit, .fit], spacing: 20) { user in
                VStack {
                    Text(user.name)
                        .font(.largeTitle)

                    Text(user.image)
                        .font(.largeTitle)
                        .background(Color.orange)
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct User: Identifiable {
    let id = UUID().uuidString
    let name: String
    let image: String
}

var array = [
    User(name: "Hello", image: "😀"),
    User(name: "No one here", image: "😆"),
    User(name: "Hello", image: "😀"),
    User(name: "No one here", image: "😆"),
    User(name: "Hello", image: "😀"),
    User(name: "No one here", image: "😆"),
    User(name: "Hello", image: "😀"),
    User(name: "No one here", image: "😆"),
    User(name: "Hello", image: "😀"),
    User(name: "No one here", image: "😆"),
]
denis-obukhov commented 4 years ago

Wrapping the grid in the ScrollView for some reason leads to the Bound preference *** tried to update multiple times per frame. errors in the console. This is a good topic to think about.

denis-obukhov commented 4 years ago

Linked with #5

denis-obukhov commented 4 years ago

I've created an isolated SwiftUI example that reproduces this issue. The reason for that is GeometryReader placed inside ScrollView that produces GeometryProxy with the size (width: ScreenWidth, height: 10) for .vertical axis, (width: 10, height: ScreenHeight) for .horizontal axis and (width: 10, height: 10) for both axes. But GeometryReader has to be used inside the Grid at least to obtain bounding width and height...

var body: some View {
    ScrollView(.vertical) { // Outer scroll view
        GeometryReader { mainGeometry in // grid inner geometry reader
            Color.red //Grid item
                .frame(width: 100, height: 100)
        }
        .background(Color.green)
    }
    .background(Color.purple)
}
denis-obukhov commented 4 years ago

@oalansari82 Talking about your example the Grid has .gridContentMode(.fill) as default implicit mode. So the Grid fills the space provided by its parent view. You have to specify space to be filled. For example:

var body: some View {
    ScrollView(.vertical) {
        Grid(array, id: \.id, tracks: [.fit, .fit], spacing: 20) { user in
            VStack {
                Text(user.name)
                    .font(.largeTitle)

                Text(user.image)
                    .font(.largeTitle)
                    .background(Color.orange)
            }
        }
        .frame(height: 1000)
    }
}
oalansari82 commented 4 years ago

@denis-obukhov That does fix it however not ideal for dynamic content unless I manually calculate each cell's size.


var body: some View {
        ScrollView(.vertical) {
            Grid(array, id: \.id, tracks: [.fit, .fit], spacing: 0) { user in
                VStack {
                    Text(user.name)
                        .font(.largeTitle)

                    Text(user.image)
                        .font(.largeTitle)
                        .background(Color.orange)
                }.background(Color.blue)
            }
            .frame(height: CGFloat(array.count * 50))
        }
    }