Dismiss @Presents modal but keep state? #3156
-
I'm presenting a custom image picker A simplified version of what I'm doing now is something like this: struct State {
@Presents var imagePicker: ImagePicker.State?
var lastImagePicker: ImagePicker.State?
}
// reducer
case .selectImageButtonTapped:
state.imagePicker = state.lastImagePicker ?? ImagePicker.State()
case .selectImage(let image):
state.lastImagePicker = state.imagePicker
state.imagePicker = nil That works, but curious if there is a better way without having to keep that redundant state around and having to manage that myself? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
If you have multiple destinations, you can remove the associated value from that case, instead. Here's an example refactor of our SyncUps app's detail feature where it preserves the edit "draft": diff --git a/Examples/SyncUps/SyncUps/SyncUpDetail.swift b/Examples/SyncUps/SyncUps/SyncUpDetail.swift
index b321ee54ac..cecb6fcba9 100644
--- a/Examples/SyncUps/SyncUps/SyncUpDetail.swift
+++ b/Examples/SyncUps/SyncUps/SyncUpDetail.swift
@@ -6,7 +6,7 @@ struct SyncUpDetail {
@Reducer(state: .equatable)
enum Destination {
case alert(AlertState<Alert>)
- case edit(SyncUpForm)
+ case edit
@CasePathable
enum Alert {
@@ -19,7 +19,14 @@ struct SyncUpDetail {
@ObservableState
struct State: Equatable {
@Presents var destination: Destination.State?
+ var edit: SyncUpForm.State
@Shared var syncUp: SyncUp
+
+ init(destination: Destination.State? = nil, syncUp: Shared<SyncUp>) {
+ self.destination = destination
+ self.edit = SyncUpForm.State(syncUp: syncUp.wrappedValue)
+ self._syncUp = syncUp
+ }
}
enum Action: Sendable {
@@ -29,6 +36,7 @@ struct SyncUpDetail {
case deleteMeetings(atOffsets: IndexSet)
case destination(PresentationAction<Destination.Action>)
case doneEditingButtonTapped
+ case edit(SyncUpForm.Action)
case editButtonTapped
case startMeetingButtonTapped
@@ -43,6 +51,9 @@ struct SyncUpDetail {
@Dependency(\.speechClient.authorizationStatus) var authorizationStatus
var body: some ReducerOf<Self> {
+ Scope(state: \.edit, action: \.edit) {
+ SyncUpForm()
+ }
Reduce { state, action in
switch action {
case .cancelEditButtonTapped:
@@ -78,14 +89,16 @@ struct SyncUpDetail {
return .none
case .doneEditingButtonTapped:
- guard case let .some(.edit(editState)) = state.destination
- else { return .none }
- state.syncUp = editState.syncUp
+ guard state.destination.is(\.edit) else { return .none }
+ state.syncUp = state.edit.syncUp
state.destination = nil
return .none
+ case .edit:
+ return .none
+
case .editButtonTapped:
- state.destination = .edit(SyncUpForm.State(syncUp: state.syncUp))
+ state.destination = .edit
return .none
case .startMeetingButtonTapped:
@@ -188,9 +201,9 @@ struct SyncUpDetailView: View {
.alert($store.scope(state: \.destination?.alert, action: \.destination.alert))
.sheet(
item: $store.scope(state: \.destination?.edit, action: \.destination.edit)
- ) { editSyncUpStore in
+ ) { _ in
NavigationStack {
- SyncUpFormView(store: editSyncUpStore)
+ SyncUpFormView(store: store.scope(state: \.edit, action: \.edit))
.navigationTitle(store.syncUp.title)
.toolbar {
ToolbarItem(placement: .cancellationAction) { |
Beta Was this translation helpful? Give feedback.
@Presents
is state-driven, so thenil
vs. non-nil
state of the wrapped value is what tells SwiftUI to hide vs. show a feature. If you want this state to stick around, you could hold onto theimagePicker
as a non-optional,Scope
-d child, instead, and then drive navigation using a simple boolean, instead.If you have multiple destinations, you can remove the associated value from that case, instead. Here's an example refactor of our SyncUps app's detail feature where it preserves the edit "draft":