skip to Main Content

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: {})

2

Answers


  1. 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)])
            }
        }
    }
    

    Animation

    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).

    Login or Signup to reply.
  2. Here is my test code that shows that the popup alert
    button action, works every time. As can be seen, there is no problem having a TextField in the alert.

    On MacOS 14.5, using Xcode 15.4, tested on real iOS 17 devices and macCatalyst.

    struct ContentView: View {
        @Environment(.dismiss) var dismiss
        
        @State private var isShowingNameInput = false
        @State private var name = ""
        
        var body: some View {
            Button("SAVE") {
                isShowingNameInput = true
            }.buttonStyle(.bordered)
            .alert("Task Name", isPresented: $isShowingNameInput) {
                TextField("", text: $name)
                Button("Confirm") {
                    print("-----> saved: (name)")
                    dismiss()
                }
                Button(role: .cancel, action: {}) {
                    Text("Cancel")
                }
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search