SwiftUI 的某些更新逻辑依赖于 Equatable 协议,当两个对象被Equatable协议判断为相等时,SwiftUI则会跳过更新,导致UI不刷新。
示例描述
例如,在修改地图点描述时,会存在无法保存成功的问题。
代码逻辑
在编辑视图中,地图点的信息从外面视图中传递进来的,当点击编辑视图的保存按钮时,会将修改的name和description字段复制给传递进来的地图点。
当前的问题在于,修改字段后,无法通过保存按钮将数据返回给地图视图。
问题代码为:
struct Location: Codable, Equatable, Identifiable {
static func == (lhs: Location, rhs: Location) -> Bool {
return lhs.id == rhs.id
}
let id: UUID
var name: String
var description: String
var latitude: Double
var longitude: Double
...
}
因为Location自定义的Equatable协议导致的,Location在定义是考虑到id是不可变的,所以id为常量且不可修改,所以自定义Equatable协议时,以作为判断是否为同一个对象。
这是因为id为UUID,UUID一致就可以默认为同一个对象,所以Equatable不需要再对比其他的元素,简化了对比方法。
但是在调用流程中,传入到编辑视图的对象修改标题和描述后,重新传给地图视图(ContentView),SwiftUI会根据Equatable协议判断两个对象是否相等,如果被判定为相等的话,Swift会跳过更新,导致UI不更新,也因此出现修改标题或描述后,仍然没有显示的原因。
因此,解决方案为修改Equatable协议,将name、description也作为是否相等进行对比:
static func == (lhs: Location, rhs: Location) -> Bool {
return lhs.id == rhs.id &&
lhs.name == rhs.name &&
lhs.description == rhs.description
}
这样,在修改视图返回对象时,发现name或者description不相等,SwiftUI进行更新,UI更新为修改后的name和description。
另一种解决方案为:
var id: UUID
将id字段改为可修改的变量。
在编辑视图中,将传入的对象的id设置一个新的UUID:
Button("Save") {
var newLocation = location
newLocation.id = UUID()
newLocation.name = name
newLocation.description = description
onSave(newLocation)
dismiss()
}
这样,再重新传回地图视图时,因为修改后的对象被赋予了一个新的UUID,因此SwiftUI认为两个对象不相等,SwiftUI进行更新操作。
以上就是本文的全部内容,通过Equatable协议简化对比后,可能会对重新修改的数据带来一些困扰,但是仍然可以通过修改Equatable协议或者将修改的对象属性进行调整,来完成对SwiftUI的更新。
相关文章
Selecting and editing map annotations:https://www.hackingwithswift.com/books/ios-swiftui/selecting-and-editing-map-annotations