Replies: 1 comment 2 replies
-
|
Can you try this instead, as - .matchedGeometryEffect(id: store.id, in: namespace)
+ .matchedGeometryEffect(id: store.state.id, in: namespace) |
Beta Was this translation helpful? Give feedback.
2 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Description
I am trying to use
matchedGeometryEffect()to apply effective animations when swapping, adding, or removing elements in a grid-like layout of multiple items.While this works without any issues in pure Swift and SwiftUI, it does not behave as expected when implemented using TCA's Reducer scopes. How should I address this problem?
OK Pattern Implementation
It doesn't use
store.scope.NG Pattern Implementation
It use
store.scope.import ComposableArchitecture import SwiftUI @Reducer private struct Content { @ObservableState struct State: Equatable { + var itemsA = IdentifiedArrayOf<Item.State>() + var itemsB = IdentifiedArrayOf<Item.State>() var flag = false } enum Action: BindableAction { case binding(BindingAction<State>) case onAppear case flagToggled + case itemsA(IdentifiedActionOf<Item>) + case itemsB(IdentifiedActionOf<Item>) } var body: some ReducerOf<Self> { BindingReducer() Reduce { state, action in switch action { case .binding: return .none case .onAppear: state.itemsA = .init(uniqueElements: [ Item.State(value: "🐤"), Item.State(value: "🐔"), ]) state.itemsB = .init(uniqueElements: [ Item.State(value: "🐦"), ]) return .none case .flagToggled: if state.flag { state.itemsB.append(state.itemsA.removeLast()) } else { state.itemsA.append(state.itemsB.removeLast()) } return .none case .itemsA: return .none case .itemsB: return .none } } + .forEach(\.itemsA, action: \.itemsA) { + Item() + } + .forEach(\.itemsB, action: \.itemsB) { + Item() + } } } struct ContentViewNG: View { @Bindable private var store: StoreOf<Content> = .init(initialState: Content.State()) { Content() } @Namespace private var namespace var body: some View { VStack(spacing: 40) { Text("NG Pattern").font(.largeTitle) HStack { + ForEach(store.scope(state: \.itemsA, action: \.itemsA)) { store in ItemView(store: store, namespace: namespace) } } HStack { + ForEach(store.scope(state: \.itemsB, action: \.itemsB)) { store in ItemView(store: store, namespace: namespace) } } Toggle(isOn: $store.flag) { Text("Move") } .onChange(of: store.flag) { _, value in store.send(.flagToggled, animation: .default) } } .padding() .onAppear { store.send(.onAppear) } } } @Reducer private struct Item { @ObservableState struct State: Equatable, Identifiable { var id: UUID = .init() var value: String } enum Action {} var body: some ReducerOf<Self> { EmptyReducer() } } private struct ItemView: View { + var store: StoreOf<Item> var namespace: Namespace.ID var body: some View { Text(store.value) .frame(maxWidth: .infinity) .padding() .overlay { RoundedRectangle(cornerRadius: 8).strokeBorder(Color(.separator), lineWidth: 2) } .matchedGeometryEffect(id: store.id, in: namespace) } }Beta Was this translation helpful? Give feedback.
All reactions