SwiftUI-FlexibleView-自动预估高度-Grid布局自动居左排列


import SwiftUI

struct FlexibleView<Data: Collection, Content: View>: View where Data.Element: Hashable {
    let data: Data //数据元
    let spacing: CGFloat //行/列间距
    let availableWidth: CGFloat //总宽度
    var alignment: HorizontalAlignment = .leading
    var targetline: Int = 3 //预估行数, 使高度能空白填充, 和其他数量不同的地方保持高度一致

    let content: (Data.Element) -> Content
    @State var elementsSize: [Data.Element: CGSize] = [:]

    var body: some View {
        if data.isEmpty {
            EmptyView()
        }else{
            ZStack(alignment: .topLeading) {
                VStack(alignment: alignment, spacing: spacing) {
                    ForEach(0..<targetline, id: \.self) { _ in
                        content(data.first!)//每个
                            .fixedSize()
                            .opacity(0)
                    }
                }
                
                VStack(alignment: alignment, spacing: spacing) {
                    ForEach(computeRows(), id: \.self) { rowElements in
                        HStack(spacing: spacing) {//每行
                            ForEach(rowElements, id: \.self) { element in
                                content(element)//每个
                                    .fixedSize()
                                    .readSize { size in
                                        elementsSize[element] = size
                                    }
                            }
                        }
                    }
                }
            }
        }
        
    }

    func computeRows() -> [[Data.Element]] {//计算行数和每个
        var rows: [[Data.Element]] = [[]]
        var currentRow = 0
        var remainingWidth = availableWidth

        for element in data {
            let elementSize = elementsSize[element, default: CGSize(width: availableWidth, height: 1)]

            if remainingWidth - (elementSize.width + spacing) >= -4 {
                rows[currentRow].append(element)
            } else {
                currentRow = currentRow + 1
                rows.append([element])
                remainingWidth = availableWidth
            }

            remainingWidth = remainingWidth - (elementSize.width + spacing)
        }

        return rows
    }
}

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

public extension View {
    func readSize(onChange: @escaping (CGSize) -> Void) -> some View {//size识别
        return background(
            GeometryReader { geometryProxy in
                Color.clear
                    .preference(key: SizePreferenceKey.self, value: geometryProxy.size)
            }
        )
        .onPreferenceChange(SizePreferenceKey.self, perform: onChange)
    }
}

以上

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容