I'm trying to get a button to shake when the user tries to log in without filling all the textfields in, and this is what I've come across so far:
struct Shake: GeometryEffect {
var amount: CGFloat = 10
var shakesPerUnit = 3
var animatableData: CGFloat
func effectValue(size: CGSize) -> ProjectionTransform {
ProjectionTransform(CGAffineTransform(translationX:
amount * sin(animatableData * .pi * CGFloat(shakesPerUnit)),
y: 0))
}
}
struct Correct: View {
@State var attempts: Int = 0
var body: some View {
VStack {
Rectangle()
.fill(Color.pink)
.frame(width: 200, height: 100)
.modifier(Shake(animatableData: CGFloat(attempts)))
Spacer()
Button(action: {
withAnimation(.default) {
self.attempts += 1
}
}, label: { Text("Login") })
}
}
}
However, this is particularly useless for a button, and even then the animation seems very off in that its pretty robotic. Can someone suggest an improvement so that I can get my button to shake?
SwiftUI uses Core Animation for rendering by default, and its performance is great for most animations. But if you find yourself creating a very complex animation that seems to suffer from lower framerates, you may want to utilize the power of Metal, which is Apple's framework used for working directly with the GPU.
SwiftUI has built-in support for animations with its animation() modifier. To use this modifier, place it after any other modifiers for your views, tell it what kind of animation you want, and also make sure you attach it to a particular value so the animation triggers only when that specific value changes.
There are two mechanisms of animating views in SwiftUI - implicit and explicit. Implicit animations are specified by attaching the Animation modifier to the view to be animated. Explicit animations are specified using withAnimation block and specifying the animations inside the closure. Implicit animations are used in this article.
Step 1: Create a new file and name it ShakeGestureManager.swift. Make sure you import the SwiftUI and the Combine framework. Step 2: Inside this file, you need to create a PassthroughSubject that is part of the Combine framework. By using this, we can notify our SwiftUI app when the device was shaken.
Although detecting shake gestures is not possible by relying on merely SwiftUI, there’s a trick to achieve the same result. Just follow these steps: Step 1: Create a new file and name it ShakeGestureManager.swift. Make sure you import the SwiftUI and the Combine framework.
By conforming to Animatable, you can effectively decouple the animation of your view from the concept of duration, as you give SwiftUI the ability to interpolate arbitrarily between two different values for animatableData.
try this
struct ContentView: View {
@State var selected = false
var body: some View {
VStack {
Button(action: {
self.selected.toggle()
}) { selected ? Text("Deselect") : Text("Select") }
Rectangle()
.fill(Color.purple)
.frame(width: 200, height: 200)
.offset(x: selected ? -30 : 0)
.animation(Animation.default.repeatCount(5).speed(6))
}
}
}
I do this to make the field shake and then gets back to it's original position:
private func signUp() {
if email.isEmpty {
withAnimation {
emailIsWrong = true
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
withAnimation {
emailIsWrong = false
}
}
return
}
}
Where emailIsWrong
is a @State variable:
@State private var emailIsWrong = false
Basically after 0.2 sec, I change the emailIsWrong
back to false so the view goes back to its position. My text field looks like this:
TextField("Email", text: $email)
.padding()
.frame(height: 45)
.background(Color.white)
.colorScheme(.light)
.offset(x: emailIsWrong ? -8 : 0)
.animation(Animation.default.repeatCount(3, autoreverses: true).speed(6))
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