1

I would like to have a button, which when pressed, changes to 'confirm'. On the second press, it executes the action. Simple enough so far.

I would also like that whenever the 'confirm' version of the button is showing, any interaction with any other view would reset the button to its original state. That could be touching the background, pressing another button, going to another tab, etc.

Very simple example of what I want to achieve.

My attempt is something like:

struct ContentView: View {
    @State private var confirmShowing = false

    var body: some View {
        ZStack {
            // Tappable background just to remove the confirmation
            Color(.systemBackground)
                .edgesIgnoringSafeArea(.all)
                .onTapGesture {
                    withAnimation {
                        confirmShowing = false
                    }
                }

            VStack {
                // Button which needs confirmation
                Button(action: {
                    if confirmShowing {
                        print("Executing")
                    }
                    withAnimation {
                        confirmShowing.toggle()
                    }
                }) {
                    Text( confirmShowing ? "Confirm" : "Do something" )
                }
                .padding()
                .background( Rectangle().foregroundColor( Color(.secondarySystemBackground) ) )
                .padding()

                // Completely different button which also should first reset confirmation
                Button("Do something else") {
                    withAnimation {
                        confirmShowing = false
                    }
                    // do some other stuff
                }
                    .padding()
                    .background( Rectangle().foregroundColor( Color(.secondarySystemBackground) ) )
                    .padding()
            }
        }
    }
}

It works for this very simple example, but it's not scalable. If I have 50 views I would have to add something to each of them just to control this one button.

Is there a better way? I'm happy to use UIViewRepresentable/UIViewControllerRepresentable.

UIKit must have this functionality right? I see it all the time in other apps and iOS in general.

SweetSour
  • 73
  • 1
  • 5

1 Answers1

0

The best way I can think of is to use an enum to control the button's state. For example. Please forgive any syntax errors, I don't have XCode open at the moment.

@State var buttonState: ButtonState = .initial

Button(action: {
    switch buttonState {
          case .initial:
              buttonState = .second
          case .second:
               //Perform whatever action after second.
          default:
               buttonState = .initial
     }
}, label: {
    switch buttonState {
        case .initial:
              Text("Some Initial Action")
        case .second:
              Text("Some Second Action")
        default:
              Text("Default Action")
    }
})

Then once you have that, create an enum to handle it.

enum ButtonState {
    case initial, second
}

Further usage is to simply add buttonState = .initial to whatever other button you want to trigger it to go back to default.

xTwisteDx
  • 2,152
  • 1
  • 9
  • 25
  • My main issue with this approach is I would have to add that line to potentially many buttons and other views. In an ideal world I would love something like a `onOtherViewTouched` modifier that I could reset the state with. – SweetSour Apr 27 '21 at 20:20
  • Have a look at this solution for the "Global Tap Gesture" - https://stackoverflow.com/questions/62400806/detect-and-forward-taps-anywhere-on-screen-in-swiftui I think this should be exactly what you're looking for. Be warned, this is going to cause problems ultimately. The button itself will also trigger that global gesture, so you need to make sure you handle that. – xTwisteDx Apr 27 '21 at 20:29