ScrollView中带有GeometryReader的SwiftUI布局

时间:2020-02-03 18:32:50

标签: layout swiftui geometryreader

我正在尝试创建一个可滚动的项目网格。我创建了一个名为GridView的自定义视图,该视图使用GeometryReader将空间分为HStacksVStacks中的列和行。但是由于某种原因,当放在ScrollView中时,其大小几乎缩小为零。在屏幕快照中,您看到GridView(带红色)及其父级VStack(带绿色)已缩小。网格内部的 项仍然可见,显示在其区域的外部,但是不能正确滚动。

为什么GridView不是容纳其项目所需的大小?如果可以,我认为该用户界面将正确滚动。

enter image description here

struct ContentView: View {
    var body: some View {
        ScrollView {
            VStack {
                Text("Section 1")
                GridView(columns: 2, items: [1, 3, 5, 7, 9, 11, 13, 15]) { num in
                    Text("Item \(num)")
                }
                .background(Color.red.opacity(0.2))
            }.background(Color.green.opacity(0.2))
        }.background(Color.blue.opacity(0.2))
    }
}

struct GridView<Content>: View where Content: View {
    var columns: Int
    let items: [Int]
    let content: (Int) -> Content

    init(columns: Int, items: [Int], @ViewBuilder content: @escaping (Int) -> Content) {
        self.columns = columns
        self.items = items
        self.content = content
    }
    var rowCount: Int {
        let (q, r) = items.count.quotientAndRemainder(dividingBy: columns)
        return q + (r == 0 ? 0 : 1)
    }
    func elementFor(_ r: Int, _ c: Int) -> Int? {
        let i = r * columns + c
        if i >= items.count { return nil }
        return items[i]
    }
    var body: some View {
        GeometryReader { geo in
            VStack {
                ForEach(0..<self.rowCount) { ri in
                   HStack {
                        ForEach(0..<self.columns) { ci in
                            Group {  
                                if self.elementFor(ri, ci) != nil {
                                    self.content(self.elementFor(ri, ci)!)
                                        .frame(width: geo.size.width / CGFloat(self.columns),
                                               height: geo.size.width / CGFloat(self.columns))
                                } else {
                                    Text("")
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

1 个答案:

答案 0 :(得分:1)

GeometryReader是一个容器视图,根据其自身大小和坐标空间定义其内容。返回其灵活的首选大小 父级布局。

struct ContentView: View {
    var body: some View {
        ScrollView {
            VStack {
                Text("Section 1")
                GridView(columns: 2, items: [1, 3, 5, 7, 9, 11, 13, 15]) { num in
                    Text("Item \(num)")
                }
                .background(Color.red.opacity(0.5))
            }.background(Color.green.opacity(0.2))
        }.background(Color.blue.opacity(0.2))
    }
}

GridView的高度是多少? 嗯...行数乘以网格单元格的高度,也就是GridView的高度除以行数...

等式没有解:-)

是的,我将网格单元的高度定义为GridView的宽度除以列数,但是SwiftUI并不那么聪明。

您必须计算GridView的高度。我将行数固定为4以简化它...

struct ContentView: View {
    var body: some View {
        GeometryReader { g in
        ScrollView {
            VStack {
                Text("Section 1")
                GridView(columns: 2, items: [1, 3, 5, 7, 9, 11, 13, 15]) { num in
                    Text("Item \(num)")
                }.frame(height: g.size.width / 2 * CGFloat(4))
                .background(Color.red.opacity(0.5))
            }.background(Color.green.opacity(0.2))
        }.background(Color.blue.opacity(0.2))
        }
    }
}

最好从GridView.body中删除几何读取器和网格单元格框架,并在内容视图中设置单元格的大小。

struct ContentView: View {
    var body: some View {
        GeometryReader { g in
        ScrollView {
            VStack {
                Text("Section 1")
                GridView(columns: 2, items: [1, 3, 5, 7, 9, 11, 13, 15]) { num in
                    Text("Item \(num)").frame(width: g.size.width / 2, height: g.size .width / 2)
                }
                .background(Color.red.opacity(0.5))
            }.background(Color.green.opacity(0.2))
        }.background(Color.blue.opacity(0.2))
        }
    }
}

如您所见,无需定义GridView的高度,现在方程式已解决。