Reputation: 2315
How do you set an action to perform when the user hits the EditButton
when it appears as "Done"? Is it even possible to do so?
Note that this is not the same as performing an action at each of the individual edits the user might do (like onDelete
or onMove
). How do you set an action to perform when the user is finished making all changes and ready to commit them?
It's necessary because I'm making all changes on a temporary copy of the model and will not commit the changes to the actual model until the user hits "Done". I am also providing a "Cancel" Button to discard the changes.
struct MyView: View {
@Environment(\.editMode) var mode
var body: some View {
VStack {
HStack {
if self.mode?.value == .active {
Button(action: {
// discard changes here
self.mode?.value = .inactive
}) {
Text("Cancel")
}
}
Spacer()
EditButton()
// how to commit changes??
}
// controls to change the model
}
}
}
Is it even possible to set an action for "Done" on the EditButton
or would I have to provide my own button to act like an EditButton
? I could certainly do that, but it seems like it should be easy to do with the EditButton
.
Upvotes: 4
Views: 8291
Reputation: 1260
New answer for XCode 12/iOS 14 using the onChange modifier.
This approach uses the SwiftUI editMode
key path available via @Environment
along with the new onChange
modifier to determine when the view enters and exits editing mode.
In your view do the following:
@Environment(\.editMode) private var editMode
...
.onChange(of: editMode!.wrappedValue, perform: { value in
if value.isEditing {
// Entering edit mode (e.g. 'Edit' tapped)
} else {
// Leaving edit mode (e.g. 'Done' tapped)
}
})
Upvotes: 13
Reputation: 3573
From what I understand, the EditButton is meant to put the entire environment in edit mode. That means going into a mode where, for example, you rearrange or delete items in a list. Or where text fields become editable. Those sorts of things. "Done" means "I'm done being in edit mode", not "I want to apply my changes." You would want a different "Apply changes," "Save," "Confirm" or whatever button to do those things.
Upvotes: 2
Reputation:
Your last comment contains a subtle change to things. Until then it sounded like you wanted both a "Done" and "Cancel" button - something very few iOS apps do.
But if you want a "Done" button with a "double-checking things, are you sure" you should be able to code such a thing by (possibly) manually going into edit mode and detecting when it's no longer in it. Save a "before" state, add a popup to when it is about to be changed, and you might give the user a rollback option.
FYI - I'm very old school. Not just PC, but mainframe. Not just Microsoft, but SAP. I certainly understand wanting to give the user one last chance before a destructive change - but not too many iOS apps do that. :-)
Upvotes: 0
Reputation: 2801
You can use the onDisappear() modifier to perform the action, on a view that you show only on edit mode. There is an example on how to do it in the tutorial of SwiftUI "Working with UI Controls":
if self.mode?.wrappedValue == .inactive {
ProfileSummary(profile: profile)
} else {
ProfileEditor(profile: $draftProfile)
.onDisappear {
self.draftProfile = self.profile
}
}
In your sample code above, since you do not have a view shown in edit mode, you can add state to check if you have clicked on "Cancel" or "Done" as below:
struct MyView: View {
@Environment(\.editMode) var mode
@State var isCancelled = false
var body: some View {
VStack {
HStack {
if self.mode?.wrappedValue == .active {
Button(action: {
// discard changes here
self.mode?.wrappedValue = .inactive
self.isCancelled = true
}) {
Text("Cancel")
}
.onAppear() {
self.isCancelled = false
}
.onDisappear() {
if (self.isCancelled) {
print("Cancel")
} else {
print("Done")
}
}
}
Spacer()
EditButton()
// how to commit changes??
}
// controls to change the model
}
}
}
Upvotes: 3