简体   繁体   中英

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()) 
                } 
            } 
        } 
    } 
 } 

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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM