如何让CoreData对象遵守Equatable协议?解决动画方法报错
解决CoreData FetchedResults的动画适配问题
问题背景
使用CoreData的FetchedResults<Article>作为ScrollView.animation(_:value:)的触发值时,出现报错:
Instance method 'animation(_:value:)' requires that 'FetchedResults<Article>' conform to 'Equatable'
解决方案
方案1:让CoreData实体类遵守Equatable
CoreData生成的实体默认不实现Equatable,手动扩展即可:
extension Article: Equatable { public static func == (lhs: Article, rhs: Article) -> Bool { // 用CoreData对象的唯一标识objectID判断相等 return lhs.objectID == rhs.objectID } }
注意:FetchedResults<T>是集合类型,即使T遵守Equatable,集合本身仍不自动支持Equatable。因此这个方案仅解决单个实体的相等判断,若要让集合符合要求,还需额外扩展FetchedResults的Equatable实现,这种方式相对繁琐,不推荐。
方案2:改用可Equatable的集合标识作为动画触发值
直接用favoriteArticles作为value不可行,换用以下两种替代值:
用元素数量触发动画:
当集合的元素数量变化时触发动画,适合只需感知增删操作的场景:.animation(.spring(), value: favoriteArticles.count)用元素唯一ID数组触发动画:
若需要感知元素增删、顺序变化等所有集合变更,可生成包含所有元素objectID的数组(NSManagedObjectID默认遵守Equatable):.animation(.spring(), value: favoriteArticles.map { $0.objectID })
方案3:绑定动画到操作本身(最推荐)
SwiftUI的ForEach对CoreData的FetchedResults原生支持动画,无需给整个ScrollView加动画,直接在修改数据的操作中包裹withAnimation即可:
修改删除按钮的代码:
Button("Delete", role: .destructive) { withAnimation(.spring()) { print("~ delete \(String(describing: selectedFavorite?.text))") guard let selectedFavorite = selectedFavorite else { return } delete(article: selectedFavorite) if favoriteArticles.isEmpty { showDeleteButton.toggle() homeViewState.showControlPanel.toggle() } } }
这种方式会让动画精准作用于被修改的行,效果更自然,同时避免了Equatable的适配问题。
内容的提问来源于stack exchange,提问作者Zhou Haibo
相关产品推荐
相关产品推荐

