Suddenly I have figured out that there is no automatic animation for non of them:

no animations for inserting, removing, reordering, etc. ☹️ Ids are unique and persistent between transitions
Question 1
How can I make them work as expected?
Question 2
Is there a simple way to have an animation for seeing the transition between lists? Like moving a row between sections of a single list.
Explaining:
Let's say I have three lists that group different states of elements of a single array:
extension Call {
enum State: Equatable {
case inProgress
case accepted
case rejected
}
}
The observable:
class CallManager: ObservableObject {
@Published var calls: [Call]
init(visits: [Call] = []) { self.calls = visits }
}
And Call is a simple Identifiable:
struct Call: Identifiable & Equatable & Hashable {
let id = UUID()
var state: State
}
By making these bindings, I have bind all lists to the core calls array:
extension CallManager {
func bindingCalls(for state: Call.State) -> Binding<[Call]> {
Binding<[Call]>(
get: { self.calls.filter { $0.state == state } },
set: { // TODO: Find a better way for this
self.calls.removeAll(where: { $0.state == state })
self.calls.append(contentsOf: $0)
}
)
}
var inProgress: Binding<[Call]> { bindingCalls(for: .inProgress) }
var accepted: Binding<[Call]> { bindingCalls(for: .accepted) }
var rejected: Binding<[Call]> { bindingCalls(for: .rejected) }
}
And here is the View code:
struct ContentView: View {
@StateObject var visitManager = CallManager(visits: [
Call(state: .inProgress),
Call(state: .accepted),
Call(state: .accepted),
Call(state: .inProgress),
Call(state: .inProgress),
Call(state: .rejected)
])
var body: some View {
HStack {
List(visitManager.inProgress) { $call in
CallView(call: $call)
}
List(visitManager.accepted) { $call in
CallView(call: $call)
}
List(visitManager.rejected) { $call in
CallView(call: $call)
}
}
}
}
struct CallView: View & Identifiable {
@Binding var call: Call
var id: UUID { call.id }
var body: some View {
Text(call.id.uuidString.prefix(15))
.foregroundColor(call.state.color)
.onTapGesture(count: 2) { call.state = .rejected }
.onTapGesture { call.state = .accepted }
}
}
extension Call.State {
var color: Color {
switch self {
case .inProgress: return .blue
case .rejected: return .red
case .accepted: return .green
}
}
}