SongJiyeon
(Song Jiyeon)
1
Hi
I'm trying to make an app which has card lists using TCA.
The cards can be removed or inserted by action.
But, not as MVVM, my cards transition methods doesn't work.
Here's my example
// App.swift
import SwiftUI
import ComposableArchitecture
struct AppState: Equatable {
var cards: [Card] = (10...20).map { Card(id: $0, number: $0) }
}
enum AppAction {
case cardCompleted(Int)
case removeCard(Int)
case getNewCards
}
struct AppEnvironment {
var mainQueue: AnySchedulerOf<DispatchQueue> = DispatchQueue.main.eraseToAnyScheduler()
}
let appReducer = Reducer<AppState, AppAction, AppEnvironment> { state, action, env in
switch action {
case .cardCompleted(let id):
guard let card = state.cards.filter({ $0.id == id }).first,
let index = state.cards.firstIndex(of: card) else {
return .none
}
state.cards[index].status = .completed
return Effect.init(value: AppAction.removeCard(index))
.delay(for: 2, scheduler: env.mainQueue)
.eraseToEffect()
case .removeCard(let index):
state.cards.remove(at: index)
return Effect.init(value: AppAction.getNewCards)
.delay(for: 0.1, scheduler: env.mainQueue)
.eraseToEffect()
case .getNewCards:
state.cards = (0...9).map { Card(id: $0, number: $0) }
return .none
}
}
struct AppView: View {
let store: Store<AppState, AppAction>
var body: some View {
WithViewStore(self.store) { viewStore in
ScrollView(.horizontal) {
HStack(spacing: 20) {
ForEach(viewStore.cards) { card in
CardView(card: card)
.onTapGesture {
viewStore.send(.cardCompleted(card.id))
}
}
}
}
}
}
}
// Card.swift
import SwiftUI
enum CardStatus {
case none
case completed
}
struct Card: Equatable, Identifiable {
var id: Int
var number: Int
var status: CardStatus = .none
}
struct CardView: View {
let card: Card
var body: some View {
VStack {
Text("Hello Card")
Text("\(card.number)")
}
.frame(width: 100, height: 150)
.background(Color.yellow)
// this part doensn't work
.transition(.asymmetric(
insertion: AnyTransition.move(edge: .trailing),
removal: AnyTransition.move(edge: .bottom)))
}
}
struct CardView_Previews: PreviewProvider {
static var previews: some View {
CardView(card: Card(id: 0, number: 0))
}
}
What I want is applying animation before removal and new card insertion after removal.
It would be nice if you let me know the solution and some knowledges about the relationship between SwiftUI and TCA(I mean, why my example didn't work).
Thanks for your help
I resolve this by adding .animation(.default).
Apple stopped animating list changes by default in Xcode 12, but as you saw, adding the view modifier (or sending an action that mutates the model in a withAnimation block) should fix things.
SongJiyeon
(Song Jiyeon)
3
Thanks for your information!
I have a question.
As you said, if it was because of Xcode 12, then My original work with MVVM works fine with just transition.
Do you know why it didn't work with TCA?
Without seeing the code I'm not sure, but I've seen that SwiftUI list animations are off by default in Xcode 12, whether or not you use TCA or a vanilla SwiftUI view model.