SwiftUI中Equatable对象内容更新时onChange未触发问题求助
问题根因
MapAnnotation是继承自MKPointAnnotation的类类型,而MKPointAnnotation属于NSObject子类,默认的Equatable实现是比对内存引用地址而非属性内容。你修改的是已存在实例的coordinate属性,实例的内存地址没有变化,因此默认比对逻辑会判定前后是同一个对象。Object结构体自动合成的Equatable逻辑是比对annotations数组的元素,数组存储类类型时,默认相等判断校验的是元素的引用是否一致。你仅修改了数组中已有实例的属性,既没有变更数组元素的引用,也没有增减元素,因此系统会判定controller.object前后值相等,onChange自然不会触发。- 若你之前仅手动实现了Swift层面的
static func ==而没有重写NSObject的isEqual方法,对于NSObject子类而言自定义的比对逻辑不会生效,系统底层比对时走的是isEqual方法而非Swift的==运算符。 - 文本可以正常刷新是因为你直接绑定了
controller.object.annotations[0].coordinate.latitude这个具体属性值,SwiftUI会直接监听该属性的变化更新对应视图,不需要走上层object的相等判断。
解决方案
方案1:重写MapAnnotation的isEqual和hash方法
这是最小改动的适配方案,符合NSObject子类的Equatable规则:
import MapKit class MapAnnotation:MKPointAnnotation { var id:String = UUID().uuidString override func isEqual(_ object: Any?) -> Bool { guard let other = object as? MapAnnotation else { return false } // 按需添加需要比对的属性 return id == other.id && coordinate.latitude == other.coordinate.latitude && coordinate.longitude == other.coordinate.longitude } override var hash: Int { // 哈希值需要和isEqual的比对属性对应 return id.hashValue ^ coordinate.latitude.hashValue ^ coordinate.longitude.hashValue } }
方案2:改为值类型实现(业务允许的前提下更推荐)
struct 是值类型,自动合成的 Equatable 会直接比对所有属性值,不需要额外处理引用问题,你可以封装结构体替代继承MKPointAnnotation:
import MapKit struct MapAnnotation: Equatable, Identifiable { let id = UUID().uuidString var coordinate: CLLocationCoordinate2D // 其他你需要的属性 }
方案3:调整onChange监听对象
如果不需要自定义Equatable逻辑,可以直接监听你关心的具体变化值:
// 直接监听纬度变化 .onChange(of: controller.object.annotations[0].coordinate.latitude) { _ in change = true }
内容的提问来源于stack exchange,提问作者lmunck
相关产品推荐
相关产品推荐

