SwiftUI中ForEach遍历AnyShape数组报Hashable兼容错误,如何解决?
解决SwiftUI中AnyShape数组遍历的Hashable兼容问题
你遇到的错误是因为ForEach使用id: \.self时,要求数组元素必须遵循Hashable协议,而AnyShape默认没有实现该协议。以下是几种可行的解决方法:
方法一:让AnyShape遵循Hashable协议
如果你的AnyShape是iOS16+提供的标准类型擦除包装器,或自定义的类似结构,可以通过扩展实现Hashable:
extension AnyShape: Hashable { public func hash(into hasher: inout Hasher) { // 利用内部包装的Shape进行哈希计算 _box.hash(into: &hasher) } public static func == (lhs: AnyShape, rhs: AnyShape) -> Bool { // 比较内部包装的Shape是否相等 lhs._box.isEqual(to: rhs._box) } }
注:若为自定义
AnyShape,将_box替换为你实际存储Shape的属性名即可。
修改后原ForEach代码可直接使用:
ForEach(viewModel.shapes, id: \.self) { shape in shape .fill(Color.blue) .frame(width: 100, height: 100) }
方法二:用Identifiable结构体封装形状
这种方法无需修改AnyShape,通过包装结构体提供唯一标识,是更稳妥的动态数组解决方案:
- 创建遵循
Identifiable的结构体:
struct IdentifiableShape: Identifiable { let id = UUID() var shape: AnyShape }
- 更新ViewModel中的数组类型:
class ViewModel: ObservableObject { var shapes: [IdentifiableShape] = [ IdentifiableShape(shape: .init(Rectangle())), IdentifiableShape(shape: .init(RoundedRectangle(cornerRadius: 10.0))) ] }
- 简化View中的
ForEach(Identifiable自动提供id):
struct ContentView: View { @StateObject var viewModel = ViewModel() var body: some View { ForEach(viewModel.shapes) { shapeItem in shapeItem.shape .fill(Color.blue) .frame(width: 100, height: 100) } } }
方法三:使用数组索引作为ID(不推荐)
仅适用于静态数组(不会增删元素),数组元素顺序变化时会导致SwiftUI错误重用视图:
struct ContentView: View { @StateObject var viewModel = ViewModel() var body: some View { ForEach(viewModel.shapes.indices, id: \.self) { index in viewModel.shapes[index] .fill(Color.blue) .frame(width: 100, height: 100) } } }
内容的提问来源于stack exchange,提问作者appfrosch
相关产品推荐
相关产品推荐

