无需显式计算宽度,在SwiftUI中实现话题标签弹性布局
SwiftUI实现无需手动计算宽度的流式话题标签布局
要实现类似CSS Flexbox的自动换行标签布局,且无需手动计算每个标签的宽度,可以利用SwiftUI的Layout协议(iOS 16+)自定义流式布局,让系统自动处理换行逻辑。
1. 自定义流式布局组件
创建一个遵循Layout协议的FlowLayout,它会自动计算子视图尺寸并完成换行布局:
import SwiftUI struct FlowLayout: Layout { var spacing: CGFloat = 8 func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { guard !subviews.isEmpty else { return .zero } let maxWidth = proposal.width ?? .infinity var currentWidth: CGFloat = 0 var currentHeight: CGFloat = 0 var lineHeights: [CGFloat] = [] for subview in subviews { let subviewSize = subview.sizeThatFits(.unspecified) if currentWidth + subviewSize.width + spacing > maxWidth, currentWidth > 0 { lineHeights.append(currentHeight) currentWidth = subviewSize.width + spacing currentHeight = subviewSize.height } else { currentWidth += subviewSize.width + spacing currentHeight = max(currentHeight, subviewSize.height) } } lineHeights.append(currentHeight) return CGSize( width: min(currentWidth - spacing, maxWidth), height: lineHeights.reduce(0, +) + (CGFloat(lineHeights.count - 1) * spacing) ) } func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { guard !subviews.isEmpty else { return } let maxWidth = bounds.width var x = bounds.minX var y = bounds.minY var currentLineHeight: CGFloat = 0 for subview in subviews { let subviewSize = subview.sizeThatFits(.unspecified) if x + subviewSize.width > maxWidth, x > bounds.minX { y += currentLineHeight + spacing x = bounds.minX currentLineHeight = 0 } subview.place( at: CGPoint(x: x, y: y + (currentLineHeight - subviewSize.height) / 2), proposal: ProposedViewSize(subviewSize) ) x += subviewSize.width + spacing currentLineHeight = max(currentLineHeight, subviewSize.height) } } }
2. 重构ContentView使用FlowLayout
去掉原来的手动分组逻辑,直接用FlowLayout包裹所有标签视图:
struct ContentView: View { let hashtags = ["#swift", "#swiftui", "#ios", "#coding", "#programming", "#development", "#mobile", "#app", "#developer", "#tech", "#xcode", "#apple", "#ui", "#ux"] var body: some View { FlowLayout(spacing: 8) { ForEach(hashtags, id: \.self) { hashtag in Text(hashtag) .padding(.vertical, 4) .padding(.horizontal, 8) .background(Color.blue) .foregroundColor(.white) .cornerRadius(8) } } .padding() } }
方案说明
- 无需手动计算任何标签宽度,
FlowLayout会自动获取每个标签的固有尺寸 - 支持自定义标签间距,通过
spacing参数调整 - 标签会在可用宽度不足时自动换行,每行高度会适配该行最高的标签
- 完全基于SwiftUI原生API实现,无需依赖第三方库
内容的提问来源于stack exchange,提问作者János
相关产品推荐
相关产品推荐

