1

我正在尝试在 SwiftUI 中创建一个新的网格结构来显示具有可变高度的卡片。在这种情况下,我在 a 中使用了几个LazyVStacksHStack并且有条件以正确的顺序显示我的数据项。这个视图单独工作,使列数适应屏幕的大小,但是当在 a 中使用它时ScrollView,它的大小计算不正确,下面的视图最终在网格下面而不是在它下面。这是我用于网格的代码:

struct StaggeredGrid<Element, Content>: View where Content: View {

    var preferredItemWidth: CGFloat
    var data: [Element] = []

    var content: (Element) -> Content

    init(preferredItemWidth: CGFloat, data: [Element], @ViewBuilder content: @escaping (Element) -> Content) {
        self.preferredItemWidth = preferredItemWidth
        self.data = data
        self.content = content
    }

    var body: some View {
        GeometryReader { geometry in
            HStack(alignment: .top, spacing: 20) {
                ForEach(1...Int(geometry.size.width / preferredItemWidth), id: \.self) { number in
                    LazyVStack(spacing: 20) {
                        ForEach(0..<data.count, id: \.self) { index in
                            if (index+1) % Int(geometry.size.width / preferredItemWidth) == number % Int(geometry.size.width / preferredItemWidth) {
                                content(data[index])
                            }
                        }
                    }
                }
            }
            .padding([.horizontal])
        }
    }
}

以及显示行为的预览:

struct StaggeredGrid_Previews: PreviewProvider {
    static var previews: some View {
        ScrollView {
            VStack {
                StaggeredGrid(preferredItemWidth: 160, data: (1...10).map{"Item \($0)"}) { item in
                    Text(item)
                        .foregroundColor(.blue)
                }
                Text("I should be below the grid")
            }
        }
    }
}

这是预览的图片: ScrollView 中的错误外观

ScrollView以及注释掉 时的图片: Expected behavior, ScrollView removed

预先感谢您提供有关我不理解的这种行为的任何帮助或线索。

4

1 回答 1

0

我引用@Asperi:“ScrollView 没有自己的大小,GeometryReader 没有自己的大小,所以你遇到了鸡蛋问题,即没有人知道要渲染的大小,所以折叠了。你必须有明确的项目框架大小在 ScrollView 中。”

在这里你必须设置你的高度StaggeredGrid,或者GeometryReader它包含的高度。在您的情况下,它的高度当然取决于其内容的高度(即 HStack)。您可以使用 Reader 读取此高度(例如其背景/叠加层的高度)。并用它来确定 GeometryReader 的帧大小

例如 :

struct StaggeredGrid<Element, Content>: View where Content: View {

    var preferredItemWidth: CGFloat
    var data: [Element] = []

    var content: (Element) -> Content

    init(preferredItemWidth: CGFloat, data: [Element], @ViewBuilder content: @escaping (Element) -> Content) {
        self.preferredItemWidth = preferredItemWidth
        self.data = data
        self.content = content
    }
    
    @State private var gridHeight: CGFloat = 100

    var body: some View {
        GeometryReader { geometry in
            HStack(alignment: .top, spacing: 20) {
                ForEach(1...Int(geometry.size.width / preferredItemWidth), id: \.self) { number in
                    LazyVStack(spacing: 20) {
                        ForEach(0..<data.count, id: \.self) { index in
                            if (index+1) % Int(geometry.size.width / preferredItemWidth) == number % Int(geometry.size.width / preferredItemWidth) {
                                content(data[index])
                            }
                        }
                    }
                }
            }
            .overlay(GeometryReader { proxy in
                Color.clear.preference(
                    key: HeightPreferenceKey.self,
                    value: proxy.size.height
                )
            })
            .onPreferenceChange(HeightPreferenceKey.self) {
                gridHeight = $0
            }
            .padding([.horizontal])
        }
        .frame(height: gridHeight)
    }
}

private struct HeightPreferenceKey: PreferenceKey {
    static let defaultValue: CGFloat = 0

    static func reduce(value: inout CGFloat,
                       nextValue: () -> CGFloat) {
        value = nextValue()
    }
}
于 2021-06-04T07:45:41.417 回答