From acf8e83cbf106b4350536d54eb46379dd86a623c Mon Sep 17 00:00:00 2001 From: Runt <qingingrunt2010@qq.com> Date: Fri, 04 Jul 2025 17:05:00 +0000 Subject: [PATCH] 输入弹框 图标修改 相机调整 --- LiveProject/views/FlowLayout.swift | 190 ++++++++++++++--------------------------------- 1 files changed, 58 insertions(+), 132 deletions(-) diff --git a/LiveProject/views/FlowLayout.swift b/LiveProject/views/FlowLayout.swift index 151cd68..038d158 100644 --- a/LiveProject/views/FlowLayout.swift +++ b/LiveProject/views/FlowLayout.swift @@ -5,163 +5,89 @@ // 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 { +struct FlowLayout: Layout { + var spacing: CGFloat = 8 + var lineSpacing: CGFloat = 8 + + func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { 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 - } + var currentLineWidth: CGFloat = 0 + var currentLineHeight: CGFloat = 0 + let maxWidth = proposal.width ?? .infinity + + for view in subviews { + let size = view.sizeThatFits(.unspecified) + if currentLineWidth + size.width > maxWidth { + width = max(width, currentLineWidth) + height += currentLineHeight + lineSpacing + currentLineWidth = size.width + currentLineHeight = size.height + } else { + currentLineWidth += size.width + spacing + currentLineHeight = max(currentLineHeight, size.height) } } + + width = max(width, currentLineWidth) + height += currentLineHeight + + return CGSize(width: width, height: height) } -} -// MARK: - 高度读取器 + func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { + var x: CGFloat = 0 + var y: CGFloat = 0 + var lineHeight: CGFloat = 0 -/// 用于读取视图高度的辅助视图 -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 + for view in subviews { + let size = view.sizeThatFits(.unspecified) + + if x + size.width > bounds.width { + x = 0 + y += lineHeight + lineSpacing + lineHeight = 0 } + + view.place( + at: CGPoint(x: bounds.minX + x, y: bounds.minY + y), + proposal: ProposedViewSize(width: size.width, height: size.height) + ) + + x += size.width + spacing + lineHeight = max(lineHeight, size.height) } } } - -// 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" + "Swift" ] @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){ + VStack{ + VStack(spacing: 20) { + FlowLayout(){ - } + ForEach(tags, id: \.self) { item in + Text(item) + .padding(.horizontal, 12) + .padding(.vertical, 6) + .background(Color.blue.opacity(0.2)) + .cornerRadius(8) + } + }.frame(alignment:.leading) + .background(Color.red) } - .padding() - .animation(.default, value: customTags) + .frame(maxWidth: .infinity,alignment:.leading) } + .background(Color.black) } private func addTag() { -- Gitblit v1.9.1