You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI无法实现简单可选标签视图,运行报错该如何解决?

修复后的可选中标签视图实现

你现有代码存在4处核心错误,修正后即可正常运行:

  1. TagViewItem的Equatable实现逻辑错误,仅对比选中状态会导致不同标签被误判为同一元素
  2. generateContent方法中引用了未定义的tag变量
  3. 错误使用inout指针传递SwiftUI状态,需替换为官方标准的Binding类型
  4. 流式布局宽度计算逻辑错误,负增量会导致标签重叠
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 14:48:01