I have a grouped list in in which I can delete items. If the groups id
property is declared as String
, everything animates correctly. If I however change it to any Hashable
the delete animation gets janky.
import SwiftUI
import Combine
class ItemGroup: Identifiable, Equatable {
static func == (lhs: ItemGroup, rhs: ItemGroup) -> Bool {
lhs.items == rhs.items
}
var id: any Hashable { "some static ID" } // 🚨 change to `String` to fix delete animation
let items: [String]
init(items: [String]) {
self.items = items
}
}
class ViewModel: ObservableObject {
private var items: [String]
@Published private(set) var groups: [ItemGroup]
private var updateItems = PassthroughSubject<Void, Never>()
init(items: [String]) {
self.items = items
self.groups = []
updateItems
.compactMap { [weak self ] _ in
guard let self = self else { return nil }
return [ItemGroup(items: self.items)]
}
.assign(to: &$groups)
updateItems.send()
}
func remove(at: IndexSet) {
items.remove(atOffsets: at)
updateItems.send()
}
}
struct PlaygroundList: View {
@StateObject var viewModel: ViewModel
init(items: [String]) {
_viewModel = StateObject(wrappedValue: ViewModel(items: items))
}
var body: some View {
return List {
ForEach(viewModel.groups) { group in
Section {
ForEach(group.items, id: \.self) { item in
Text(item)
}.onDelete { viewModel.remove(at: $0) }
} header: {
Text("Section")
}
}
}
}
}
struct PlaygroundList_Previews: PreviewProvider {
static var previews: some View {
PlaygroundList(items: ["A", "B", "C", "D", "E", "F"])
}
}
I'm aware that there are a few points one could argue about in the code above. But those are not the point. The code only exists in this form to serve as illustration for my question: Why does it make a difference if id
is declared as any Hashable
instead of as String
?