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())
}
}
}
}
}
For example: you can run actionOfButton() from the place you want programmatically tap the Button, it would work the same.
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!") }
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.
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:
Image
and overlay a Button
that spans its entire area using a spacer.ButtonStyle
we apply exposes the internal isPressed
variable of the button to our IconButton
view.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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With