From bf9e4680eb466bcb7c9cb1bef567252bb1f2bb7d Mon Sep 17 00:00:00 2001
From: Runt <qingingrunt2010@qq.com>
Date: Fri, 27 Jun 2025 05:32:55 +0000
Subject: [PATCH] 1

---
 LiveProject/views/FlowLayout.swift |  193 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 193 insertions(+), 0 deletions(-)

diff --git a/LiveProject/views/FlowLayout.swift b/LiveProject/views/FlowLayout.swift
index f3ee86a..151cd68 100644
--- a/LiveProject/views/FlowLayout.swift
+++ b/LiveProject/views/FlowLayout.swift
@@ -4,4 +4,197 @@
 //
 //  Created by 倪路朋 on 6/26/25.
 //
+import SwiftUI
+import SwiftUI
 
+/// 完全自定义的自动换行布局容器
+struct FlowLayout<Data: RandomAccessCollection, Content: View>: View where Data.Element: Hashable {
+    // MARK: - 属性
+    
+    /// 要显示的数据集合
+    let data: Data
+    /// 水平间距
+    let horizontalSpacing: CGFloat
+    /// 垂直间距
+    let verticalSpacing: CGFloat
+    /// 对齐方式
+    let alignment: HorizontalAlignment
+    /// 内容构建闭包
+    let content: (Data.Element) -> Content
+    /// 总高度状态
+    @State private var totalHeight: CGFloat = 0
+    
+    // MARK: - 初始化
+    
+    /// 初始化FlowLayout
+    /// - Parameters:
+    ///   - data: 要显示的数据集合
+    ///   - horizontalSpacing: 水平间距,默认为8
+    ///   - verticalSpacing: 垂直间距,默认为8
+    ///   - alignment: 对齐方式,默认为.leading
+    ///   - content: 内容构建闭包
+    init(
+        _ data: Data,
+        horizontalSpacing: CGFloat = 8,
+        verticalSpacing: CGFloat = 8,
+        alignment: HorizontalAlignment = .leading,
+        @ViewBuilder content: @escaping (Data.Element) -> Content
+    ) {
+        self.data = data
+        self.horizontalSpacing = horizontalSpacing
+        self.verticalSpacing = verticalSpacing
+        self.alignment = alignment
+        self.content = content
+    }
+    
+    // MARK: - 主体视图
+    
+    var body: some View {
+        GeometryReader { geometry in
+            self.contentView(in: geometry)
+                .background(
+                    HeightReader(height: $totalHeight)
+                )
+        }
+        .frame(height: totalHeight)
+    }
+    
+    // MARK: - 私有方法
+    
+    /// 构建内容视图
+    private func contentView(in geometry: GeometryProxy) -> some View {
+        var width: CGFloat = 0
+        var height: CGFloat = 0
+        var lastHeight: CGFloat = 0
+        
+        return ZStack(alignment: Alignment(horizontal: alignment, vertical: .top)) {
+            ForEach(data.map { $0 }, id: \.self) { item in
+                content(item)
+                    .padding(.trailing, horizontalSpacing)
+                    .padding(.bottom, verticalSpacing)
+                    .alignmentGuide(.leading) { dimensions in
+                        // 检查是否需要换行
+                        if abs(width - dimensions.width) > geometry.size.width {
+                            width = 0
+                            height += lastHeight + verticalSpacing
+                        }
+                        
+                        let result = width
+                        
+                        // 更新宽度计算
+                        if item == data.last {
+                            width = 0 // 重置为0,最后一项
+                        } else {
+                            width -= dimensions.width + horizontalSpacing
+                        }
+                        
+                        // 记录当前行高度
+                        lastHeight = dimensions.height
+                        return result
+                    }
+                    .alignmentGuide(.top) { dimensions in
+                        let result = height
+                        
+                        // 如果是最后一项,更新总高度
+                        if item == data.last {
+                            height += lastHeight + verticalSpacing
+                        }
+                        
+                        return result
+                    }
+            }
+        }
+    }
+}
+
+// MARK: - 高度读取器
+
+/// 用于读取视图高度的辅助视图
+private struct HeightReader: View {
+    @Binding var height: CGFloat
+    
+    var body: some View {
+        GeometryReader { geometry in
+            Color.clear
+                .preference(
+                    key: HeightPreferenceKey.self,
+                    value: geometry.size.height
+                )
+        }
+        .onPreferenceChange(HeightPreferenceKey.self) { newHeight in
+            DispatchQueue.main.async {
+                self.height = newHeight
+            }
+        }
+    }
+}
+
+// MARK: - 高度偏好键
+
+/// 用于传递高度值的PreferenceKey
+private struct HeightPreferenceKey: PreferenceKey {
+    static var defaultValue: CGFloat = 0
+    
+    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
+        value = nextValue()
+    }
+}
+
+// MARK: - 使用示例
+
+struct FlowLayoutExample: View {
+    let tags = [
+        "Swift", "SwiftUI", "UIKit", "Combine", "Core Data",
+        "Xcode", "Interface Builder", "Core Animation", "ARKit",
+        "Metal", "Core ML", "Vision", "MapKit", "CloudKit"
+    ]
+    
+    @State private var newTag = ""
+    @State private var customTags = ["自定义标签1", "自定义标签2"]
+    
+    var body: some View {
+        VStack {
+            FlowLayout(customTags + tags, horizontalSpacing: 10, verticalSpacing: 10) { tag in
+                MButton(text:tag){
+                    
+                }
+            }
+            .padding()
+            .animation(.default, value: customTags)
+        }
+    }
+    
+    private func addTag() {
+        withAnimation {
+            customTags.append(newTag)
+            newTag = ""
+        }
+    }
+}
+
+/// 标签视图
+struct TagView: View {
+    let text: String
+    
+    var body: some View {
+        Text(text)
+            .padding(.horizontal, 12)
+            .padding(.vertical, 8)
+            .background(Color.blue.opacity(0.2))
+            .foregroundColor(.blue)
+            .cornerRadius(10)
+            .overlay(
+                RoundedRectangle(cornerRadius: 10)
+                    .stroke(Color.blue, lineWidth: 1)
+            )
+            .fixedSize(horizontal: true, vertical: false) // 确保文本不被截断
+    }
+}
+
+// MARK: - 预览
+
+struct FlowLayout_Previews: PreviewProvider {
+    static var previews: some View {
+        FlowLayoutExample()
+    }
+}

--
Gitblit v1.9.1