I am trying to animate the button when user taps on it. It offsets on the side making it look like it's pressed.
If you look at the images you should see why I want to animate it by offsetting it since I have a background behind the button which is offset and the button should match that frame when clicked.
Currently the button animates as shown in the picture when tapped but all of the button animates getting pressed and they don't return to original position after the click happens.
Button before clicked
Button after click
Below is the buttons array:
@State private var isClicked = false
let buttons: [[CalcButton]] = [
[.clear, .plusMinus, .percent, .divide],
[.seven, .eight, .nine, .multiply],
[.four, .five, .six, .minus],
[.one, .two, .three, .add],
[.zero, .doubleZero, .decimal, .equals]
]
ForEach(buttons, id: \.self) { row in
HStack(spacing: 20) {
ForEach(row, id: \.self) { item in
Button(action: {
withAnimation {
self.animation()
}
} , label: {
ZStack {
Rectangle()
.frame(width: buttonWidth(button: item), height: buttonHeight())
.foregroundColor(.backgroundColor)
.offset(x: 7.0, y: 7.0)
Rectangle()
.frame(width: buttonWidth(button: item), height: buttonHeight())
.foregroundColor(.white)
Text(item.rawValue)
.font(.custom("ChicagoFLF", size: 27))
.frame(width: buttonWidth(button: item), height: buttonHeight())
.foregroundColor(.backgroundColor)
.border(Color.backgroundColor, width: 4)
.offset(x: isClicked ? 7 : 0, y: isClicked ? 7 : 0)
}
})
}
}
.padding(.bottom, 10)
}
This is the function to toggle the isClicked state variable
func animation() {
self.isClicked.toggle()
}
You need a selection state for each button. so better to create a custom button.
Here is the demo version code.
Custom Button view
struct CustomButton: View {
var text: String
var action: () -> Void
@State private var isPressed = false
var body: some View {
Button(action: {
// Do something..
}, label: {
ZStack {
Rectangle()
.foregroundColor(.black)
.offset(x: 7.0, y: 7.0)
Rectangle()
.foregroundColor(.white)
Text(text)
.frame(width: 50, height: 50)
.foregroundColor(.black)
.border(Color.black, width: 4)
.offset(x: isPressed ? 7 : 0, y: isPressed ? 7 : 0)
}
})
.buttonStyle(PlainButtonStyle())
.simultaneousGesture(
DragGesture(minimumDistance: 0)
.onChanged({ _ in
// Comment this line if you want stay effect after clicked
isPressed = true
})
.onEnded({ _ in
isPressed = false
// // Uncomment below line and comment above line if you want stay effect after clicked
//isPressed.toggle()
action()
})
)
.frame(width: 50, height: 50)
}
}
Usage:
struct DemoView: View {
var body: some View {
HStack(spacing: 10) {
ForEach(0..<10) { index in
CustomButton(text: index.description) {
print("Action")
}
}
}
}
}
If you want to keep your effect after clicked. Just replace this code part.
.simultaneousGesture(
DragGesture(minimumDistance: 0)
.onChanged({ _ in
})
.onEnded({ _ in
isPressed.toggle()
action()
})
)