微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

SwiftUI ScrollView 不计算自定义 StaggeredGrid 高度

如何解决SwiftUI ScrollView 不计算自定义 StaggeredGrid 高度

我正在尝试在 SwiftUI 中创建一个新的网格结构来显示具有可变高度的卡片。就此而言,我在 <body> <canvas id="chartJSContainer" width="600" height="400"></canvas> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.2.0/chart.js"></script> </body> 中使用了多个 Lazyvstack,并且有一个条件以正确的顺序显示我的数据项。此视图单独工作,根据屏幕大小调整列数,但在 HStack 中使用它时,其大小计算不正确,随后的视图最终位于网格下方而不是下方。这是我用于网格的代码

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])
        }
    }
}

这是预览的图片Wrong appearance in a ScrollView

还有一张 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") } } } } 被注释掉的图片Expected behavior,ScrollView removed

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

解决方法

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

在这里您必须设置 StaggeredGrid 的高度,或者它包含的 GeometryReader。 在您的情况下,它的高度当然取决于其内容的高度(即 HStack)。您可以使用阅读器读取此高度(例如其背景/覆盖层的高度)。并用它来确定 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()
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。