Search code examples
iosswiftswiftuiuisegmentedcontrolpicker

Move to other Picker View tabs only after the user taps on a button in an alert view - SwiftUI


What I would like to be able to do is that when the user taps on a color tab, first present an alert view asking before actually moving to the other tab and if the user decides to Cancel it will not move to the other tab but if the user taps the OK button, the Picker will actually move to the selected tab.

How can I move to a different color tab only after the user taps on the YES button in the alert view?

    struct ContentView: View {
        @State private var favoriteColor = 0
        @State private var showAlert = false

        var body: some View {
            VStack {
                Picker("What is your favorite color?", selection: $favoriteColor) {
                    Text("Red").tag(0)
                    Text("Green").tag(1)
                }
                .pickerStyle(.segmented)

                .onChange(of: favoriteColor) { tag in
                    showAlert = true
                }

                .alert("By changing color also modifieds other things...change color?", isPresented: $showAlert) {
                    Button("YES") { 
                        // move to selected color tab
                    }
                    Button("Cancel") {
                        // do nothing
                    }
                }
            }
        }
    }

Solution

  • SwiftUI is reactive world, means our code receives control post-factum... use UIKit representable instead for that...

    ... Or, here is a possible workaround with proxy binding (however not sure if behavior persists in all OS versions)

    Tested with Xcode 13.4 / iOS 15.5

    demo

    Main part:

    var selected: Binding<Int> {     // << proxy !!
        Binding<Int>(
            get: { favoriteColor },
            set: {
                toConfirm = $0      // store for verification !!
                showAlert = true    // show alert !!
            }
        )
    }
    var body: some View {
        VStack {
            // << bind to proxy binding here !!
            Picker("What is your favorite color?", selection: selected) { 
                Text("Red").tag(0)
                Text("Green").tag(1)
            }
            .pickerStyle(.segmented)
            .alert("By changing color also modifieds other things...change color?", isPresented: $showAlert, presenting: toConfirm) { color in   // << inject for verification !!
                Button("YES") {
                    favoriteColor = color   // << do verification here !!
                }
                Button("Cancel") {
                    // do nothing
                }
            }
        }
    

    Test code on GitHub