Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a better way to implement a shake animation in swiftui?

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?

like image 521
b4younoit Avatar asked May 05 '20 17:05

b4younoit


People also ask

Does SwiftUI use Core Animation?

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.

How do I make an animation in SwiftUI?

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.

How do I animate a view in SwiftUI?

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.

How to create shakegesturemanager in SwiftUI?

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.

How to detect shake gestures in SwiftUI?

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.

What are the benefits of using animatable in SwiftUI?

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.


Video Answer


2 Answers

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))
        }
    }
}
like image 147
Chris Avatar answered Nov 15 '22 09:11

Chris


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))
like image 31
fullmoon Avatar answered Nov 15 '22 08:11

fullmoon