What I want to achieve: when clicking the save button, it pops up a box asking for the name. after user gives a name and hits the confirm button, it performs some actions and dismisses the view (return to the parent view).
here's my code, however, it doesn't perform any actions when I save and confirm the first time. All following attempts works. any idea why it doesn't work for the first time?
Button(action: {
self.isShowingNameInput = true
}) {
Text("SAVE")
}
.alert("Task Name", isPresented: self.$isShowingNameInput, actions: {
TextField("", text: self.$options.name)
Button(action: {
log("Saved \(self.options.name)")
self.presentationMode.wrappedValue.dismiss()
}) {
Text("Confirm")
}.disabled(self.options.name.isEmpty)
Button(role: .cancel, action: {}) {
Text("Cancel")
}
}, message: {})
I am not sure if you can have a TextField
in the actions
of an alert. In general, the data for an alert should not change after presentation occurs and this also applies to the values of state variables that you show in the content of the alert. See Update text inside alert message which relates to a similar issue.
As a workaround, you could consider showing a sheet instead:
struct ConfirmationDialog: View {
@Binding var options: Options
@Environment(\.dismiss) private var dismiss
var body: some View {
VStack {
Spacer()
Text("Please enter your name")
TextField("Name", text: $options.name)
.textFieldStyle(.roundedBorder)
Spacer()
Button {
print("Saved \(options.name)")
dismiss()
} label: {
Text("Confirm")
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.disabled(options.name.isEmpty)
Button(role: .cancel) {
dismiss()
} label: {
Text("Cancel")
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
}
.padding()
}
}
struct ContentView: View {
@State private var isShowingNameInput = false
@State private var options = Options()
var body: some View {
Button("SAVE") {
isShowingNameInput = true
}
.sheet(isPresented: $isShowingNameInput) {
ConfirmationDialog(options: $options)
.presentationDetents([.height(250)])
}
}
}
Alternatively, you could implement a custom alert. The answer to the post mentioned above shows an example of how this can be done (it was my answer).
EDIT Ref. the questions in your comment: the reason for implementing the dialog as a separate View
is so that dismiss
can be used to dismiss the sheet, which I thought was what you were trying to do before. See the documentation for more details about dismiss. However, you don't have to implement it this way - if the sheet would be contained in the view that shows it then it could be dismisssed just by setting the state variable isShowingNameInput
back to false. The dialog buttons could then call the dismiss
action associated with the containing view instead, in order to go back to the parent view. There was no parent view in your example though.
EDIT2 To have the text field auto-focus, try adding a FocusState
variable which you then set to true in .onAppear
:
@FocusState private var isFocused: Bool
TextField("Name", text: $options.name)
.textFieldStyle(.roundedBorder)
.focused($isFocused)
.onAppear { isFocused = true }