Closed technicated closed 4 years ago
Hi @technicated. The reason you're not seeing an animation is because withAnimation
blocks are performed synchronously, but the sorting is happening later. iOS animates sorting by default, but it appears that macOS does not.
If you want the result of an effect to be animated you must animate from state. One way to do so is by adding animation
view modifiers to the view hierarchy. In this particular case you can animate the List
and then reset this modifier on the list's content so that not all changes in each row animates:
List {
Section(header: Header(store: store)) {
...
}
.animation(nil) // don't animate every list item
}
.animation(.default) // animate the state of the list, though, including ordering
Yes, it works! I didn't know about this difference, I guess every day we learn something new 😊
I'll close the issue adding that using .animation
it works on both platforms and even without using withAnimation
in the action closure.
Hi @technicated. The reason you're not seeing an animation is because
withAnimation
blocks are performed synchronously, but the sorting is happening later. iOS animates sorting by default, but it appears that macOS does not.If you want the result of an effect to be animated you must animate from state. One way to do so is by adding
animation
view modifiers to the view hierarchy. In this particular case you can animate theList
and then reset this modifier on the list's content so that not all changes in each row animates:List { Section(header: Header(store: store)) { ... } .animation(nil) // don't animate every list item } .animation(.default) // animate the state of the list, though, including ordering
'animation' was deprecated in iOS 15.0: Use withAnimation or animation(_:value:) instead.
@stephencelis Going through the Tour of TCA now in 2022, it seems as SwiftUI has evolved enough since this original issue that we're in need of some new solutions for this sorting animation problem.
Xcode 13.3 iOS 15.4 simulator
iOS animates sorting by default, but it appears that macOS does not.
It appears now that iOS does not either. That's not a huge problem, provided your suggestion above to add .animation(_:)
still works.
.animation(_:)
modifier is deprecated in iOS 15.0 in favor of .animation(_:value:)
.Here's what I found:
I downloaded the 0102-swift-composable-architecture-tour-pt3 example code, made no code changes, and am seeing that the list items are not animating their sorting:
In an attempt to solve the first problem, I then made the following updates to these lines within ContentView based on your previous reply above:
List {
ForEachStore(
self.store.scope(state: \.todos, action: AppAction.todo(index:action:)),
content: TodoView.init(store:)
)
.animation(nil)
}
.animation(.default)
Which leads to this:
As you can see, the animation of sorting now occurs, however, we're still faced with problems:
.animation(_:)
being deprecated. If we were to use the new .animated(_:value:)
modifier, we need something for value
. viewStore.todos
makes a poor choice here because we're really just interested in watching the order of the elements in the todos
array not the todos themselves.I was able to solve both of these problems by introducing a new computed property to the AppState that holds onto an array of just the Todo.IDs:
struct AppState: Equatable {
var todos: [Todo] = []
var todoIDs: [Todo.ID] {
self.todos.map(\.id)
}
}
This should only change when a new todo is added or when the todos are sorted. With this in place, I then did this:
List {
ForEachStore(
self.store.scope(state: \.todos, action: AppAction.todo(index:action:)),
content: TodoView.init(store:)
)
.animation(nil, value: viewStore.todoIDs)
}
.animation(.default, value: viewStore.todoIDs)
As you can see, this works as I'd hoped:
Introducing a new bit of state just to get animations behaving in this way works, but feels a bit messy. Is there some other solution that I'm missing?
Describe the bug On macOS, a
ForEach
inside aList
driven by anArray
does not animate a sort if the operation is performed by anEffect
. The animation correctly shows if the sort operation is performed usingsend
.To Reproduce Just use this code as your
ContentView
. On macOS this will not work, on iOS it will instead animate.Expected behavior On macOS as on iOS, the
List
rearranges with an animation even after theEffect
-ful sort.Environment