SwiftUI - 动态的LazyHGrid行高

4
我正在创建一个垂直布局,其中包含可滚动的水平LazyHGrid。问题在于LazyHGrid中的视图可能具有不同的高度(主要是由于动态文本行),但网格始终根据网格中的第一个元素计算其自身的高度:

enter image description here

我想要的是根据可见项更改浅红色矩形的大小,因此当有较小的可见项时,它应该看起来像这样:

enter image description here

当有更大的项目时,它应该看起来像这样:

enter image description here

这是导致第一张图片状态的代码:
struct TestView: PreviewProvider {

    static var previews: some View {
        ScrollView {
            VStack {
                Color.blue
                    .frame(height: 100)
                ScrollView(.horizontal) {
                    LazyHGrid(
                        rows: [GridItem()],
                        alignment: .top,
                        spacing: 16
                    ) {
                        Color.red
                            .frame(width: 64, height: 24)
                        ForEach(Array(0...10), id: \.self) { value in
                            Color.red
                                .frame(width: 64, height: CGFloat.random(in: 32...92))
                        }
                    }.padding()
                }.background(Color.red.opacity(0.3))
                Color.green
                    .frame(height: 100)
            }
        }
    }
}

我想要的类似的东西可以通过以下方式实现:

extension View {
    func readSize(edgesIgnoringSafeArea: Edge.Set = [], onChange: @escaping (CGSize) -> Void) -> some View {
        background(
            GeometryReader { geometryProxy in
                SwiftUI.Color.clear
                    .preference(key: ReadSizePreferenceKey.self, value: geometryProxy.size)
            }.edgesIgnoringSafeArea(edgesIgnoringSafeArea)
        )
        .onPreferenceChange(ReadSizePreferenceKey.self) { size in
            DispatchQueue.main.async { onChange(size) }
        }
    }
}

struct ReadSizePreferenceKey: PreferenceKey {
    static var defaultValue: CGSize = .zero
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {}
}

struct Size: Equatable {
    var height: CGFloat
    var isValid: Bool
}

struct TestView: View {

    @State private var sizes = [Int: Size]()
    @State private var height: CGFloat = 32
    static let values: [(Int, CGFloat)] =
        (0...3).map { ($0, CGFloat(32)) }
        + (4...10).map { ($0, CGFloat(92)) }

    var body: some View {
        ScrollView {
            VStack {
                Color.blue
                    .frame(height: 100)
                ScrollView(.horizontal) {
                    LazyHGrid(
                        rows: [GridItem(.fixed(height))],
                        alignment: .top,
                        spacing: 16
                    ) {
                        ForEach(Array(Self.values), id: \.0) { value in
                            Color.red
                                .frame(width: 300, height: value.1)
                                .readSize { sizes[value.0]?.height = $0.height }
                                .onAppear {
                                    if sizes[value.0] == nil {
                                        sizes[value.0] = Size(height: .zero, isValid: true)
                                    } else {
                                        sizes[value.0]?.isValid = true
                                    }
                                }
                                .onDisappear { sizes[value.0]?.isValid = false }
                        }
                    }.padding()
                }.background(Color.red.opacity(0.3))
                Color.green
                    .frame(height: 100)
            }
        }.onChange(of: sizes) { sizes in
            height = sizes.filter { $0.1.isValid }.map { $0.1.height }.max() ?? 32
        }
    }

}

enter image description here

...但是你会发现它有点卡顿且有点复杂,难道没有更好的解决方案吗?谢谢大家!


我曾经遇到过类似的问题,最终我使用了一个 HStack,在预先计算所有高度并通过首选项键应用更大的浮点高度到所有视图。但我不太确定你如何通过懒加载来实现这一点。 - snksnk
你可以尝试使用“布局(Layout)”,但我认为这不是一种懒惰的方法。 - lorem ipsum
1个回答

0
在LazyHGrid中,一行的高度由最高单元格的高度驱动。 根据您提供的示例,如果数据源仅在开始时具有较小的大小,则其将仅显示较小的高度。
除非第一次呈现知道存在不同的高度,否则使用较大的值作为高度。
您期望的UI行为是高度会自动切换吗?还是从开始使用最高高度。

嘿,你解释了我在问题中已经解释过的内容,所以这不是能回答我的问题的答案。我的行为是根据可见内容进行切换,就像我描述的那样。 - Robert Dresler
抱歉,我只是想澄清一下需求。所以您期望的效果是尺寸切换的流畅性吗? - LIN SUNG HAO
我期望的效果已经在问题中描述了。我需要一个不卡顿且比我的硬计算更好的解决方案。 - Robert Dresler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接