Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to change a button image when the button is pressed in swiftui

I would like to change the image name when the button is pressed.

This is my structure so far:

struct ContentView: View { 
    @State var matrix = [Int] var 
    body: some View { 
        VStack { 
            Text("Test") 
            HStack(alignment: .center, spacing: 8) { 
                Button(action: { 
                    **
                    // call a func that will do some check and 
                    // change the image from Blue.png to Red.png
                    ** 
                }) { 
                    Image("Blue") 
                        .renderingMode(.original)
                        .resizable()                               
                        .aspectRatio(contentMode: .fill)
                        .clipShape(Circle()) 
                } 
            } 
        } 
    } 
 } 
like image 405
Fred Cox Avatar asked Mar 01 '20 22:03

Fred Cox


People also ask

How do I programmatically click a button in SwiftUI?

For example: you can run actionOfButton() from the place you want programmatically tap the Button, it would work the same.

How do I create a button action in SwiftUI?

SwiftUI's button is similar to UIButton , except it's more flexible in terms of what content it shows and it uses a closure for its action rather than the old target/action system. To create a button with a string title you would start with code like this: Button("Button title") { print("Button tapped!") }

What is a button SwiftUI?

A Button is a type of control that performs an action when it is triggered. In SwiftUI, a Button typically requires a title text which is the text description of your button, and an action function that will handle an event action when triggered by the user.


1 Answers

I've had a look around for a good solution to this, but they all have their downsides. An approach I have very often seen (here) revolves around creating a custom ViewModifier with a DragGesture on the button content that recognizes user touch and release. There are many issues with this solution, mainly that the gesture is given priority over any other gesture. This makes it impossible to use the buttons in a ScrollView.

From iOS 13.0+, Apple provides a ButtonStyle protocol primarily made for swiftly styling buttons. The protocol exposes an isPressed variable that perfectly reflects the button state. By this I mean that when you press and hold a button and drag your finger outside its view, the variable changes from true to false just as you would expect. This is not the case with the previous solutions I linked to.

The solution to the problem is to first define a new ButtonStyle with a variable binding isPressed:

struct IsPressedRegisterStyle : ButtonStyle {
    @Binding var isPressed : Bool
    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
            .onChange(of: configuration.isPressed, perform: {newVal in
                isPressed = newVal
            })
    }
}

And then use the style with this:

struct IconButton : View {
    @State var width: CGFloat? = nil
    @State var height: CGFloat? = nil
    let action : () -> Void
    
    @State private var isPressed : Bool = false
    
    var body : some View {
        Image(isPressed ? "someImage" : "someOtherImage")
            .renderingMode(.original)
            .resizable()
            .scaledToFit()
            .frame(width: width, height: height)
            .overlay{
                GeometryReader{proxy in
                    Button(
                        action:{
                            action()
                        },
                        label: {
                            Spacer()
                                .frame(width: proxy.size.width, height: proxy.size.height)
                                .contentShape(Rectangle())
                        })
                        .buttonStyle(IsPressedRegisterStyle(isPressed: $isPressed))
                }
            }
    }
}

Note the use of the custom ButtonStyle at the bottom of its definition.

Just a quick walkthrough of what exactly happens here:

  • We create an Image and overlay a Button that spans its entire area using a spacer.
  • Pressing the image triggers the overlaying button.
  • The custom ButtonStyle we apply exposes the internal isPressed variable of the button to our IconButton view.
  • We set the button image according to the exposed value.

You instantiate a button like this:

// arbitrary values for width and height
IconButton(width: 200, height: 70){
    print("Button Pressed")
}

I have not tested this code for too much, so please let me know if there are any downsides to using this.

Cheers!

like image 199
Hkon Avatar answered Nov 15 '22 01:11

Hkon