SwiftUI无法实现简单可选标签视图,运行报错该如何解决?
修复后的可选中标签视图实现
你现有代码存在4处核心错误,修正后即可正常运行:
TagViewItem的Equatable实现逻辑错误,仅对比选中状态会导致不同标签被误判为同一元素generateContent方法中引用了未定义的tag变量- 错误使用
inout指针传递SwiftUI状态,需替换为官方标准的Binding类型 - 流式布局宽度计算逻辑错误,负增量会导致标签重叠
import SwiftUI struct TagViewItem: Hashable, Identifiable { let id = UUID() var title: String var isSelected: Bool static func == (lhs: TagViewItem, rhs: TagViewItem) -> Bool { lhs.id == rhs.id } func hash(into hasher: inout Hasher) { hasher.combine(id) } } struct TagView: View { // 若需要父视图同步选中状态可改为@Binding var tags: [TagViewItem] @State var tags: [TagViewItem] @State private var totalHeight = CGFloat.zero // 可替换为你自己的全局颜色定义 private enum Colors { static let primaryBarBackground = Color.white static let textColor = Color.black static let primaryBlue = Color.blue } var body: some View { VStack { GeometryReader { geometry in generateContent(in: geometry) } } .frame(height: totalHeight) } private func generateContent(in g: GeometryProxy) -> some View { var width = CGFloat.zero var height = CGFloat.zero return ZStack(alignment: .topLeading) { ForEach($tags) { $tag in item(for: tag.title, isSelected: $tag.isSelected) .padding([.horizontal, .vertical], 4) .alignmentGuide(.leading, computeValue: { d in if (width + d.width) > g.size.width { width = 0 height -= d.height } let result = width if tag.id == tags.last?.id { width = 0 } else { width += d.width } return result }) .alignmentGuide(.top, computeValue: {d in let result = height if tag.id == tags.last?.id { height = 0 } return result }) } } .background(viewHeightReader($totalHeight)) } private func item(for text: String, isSelected: Binding<Bool>) -> some View { Text(text) .foregroundColor(isSelected.wrappedValue ? Colors.primaryBarBackground : Colors.textColor) .padding(.horizontal, 16) .lineLimit(1) .background(isSelected.wrappedValue ? Colors.primaryBlue : Colors.primaryBarBackground) .frame(height: 36) .cornerRadius(18) .overlay(Capsule().stroke(Colors.primaryBlue, lineWidth: 1)) .onTapGesture { isSelected.wrappedValue.toggle() } } private func viewHeightReader(_ binding: Binding<CGFloat>) -> some View { return GeometryReader { geometry -> Color in let rect = geometry.frame(in: .local) DispatchQueue.main.async { binding.wrappedValue = rect.size.height } return .clear } } } // 预览示例 struct TagView_Previews: PreviewProvider { static var previews: some View { TagView(tags: [ TagViewItem(title: "标签1", isSelected: false), TagViewItem(title: "较长的标签2", isSelected: true), TagViewItem(title: "标签3", isSelected: false), TagViewItem(title: "超长的测试标签4", isSelected: false), TagViewItem(title: "标签5", isSelected: true) ]) .padding() .background(Color.gray.opacity(0.1)) } }
用法说明
直接传入带isSelected状态的TagViewItem数组即可,点击标签会自动切换选中状态,支持自动换行的流式布局。如果需要父视图同步选中状态,将@State var tags改为@Binding var tags即可。
内容的提问来源于stack exchange,提问作者Ahmadreza
相关产品推荐
相关产品推荐

