skip to Main Content

I’m trying to animate a bell icon with the rotation effect but it only animates with the specified angle one way:

struct ContentView: View {
    @State var alarmSet = false
    
    var body: some View {
        ZStack {
            Color.black
            VStack {
                Image(systemName: "bell.fill")
                    .font(.system(size: 250))
                    .foregroundColor(.white)
                    .rotationEffect(.degrees(reminderSet ? 10 : 0), anchor: .top)
                    .animation(.interpolatingSpring(mass: 0.5, stiffness: 170, damping: 2.5, initialVelocity: 0), value: alarmSet)
                Button(action: {
                        alarmSet.toggle()
                }, label: {
                    Text(alarmSet ? "Alarm Set" : "Wake Me")
                        .font(.largeTitle)
                        .foregroundStyle(.white)
                })
                .padding(.top, 20)
            }
            .background(.black)
        }
        .ignoresSafeArea()
    }
}

With the code above, the bell animates between left side & center, I’d like to get it to (1.) swing with the same angle towards the right as well & (2.) swing this way thrice in total and settle to resting state. The interpolating spring animation causes the bell to swing more than thrice as it comes to its resting state. Any help is appreciated.

2

Answers


  1. You can do something like:

    withAnimation {
        raiseTheBell = true // 👈 Move the bell to the falling state
    } completion: {
        withAnimation(.interpolatingSpring(mass: 0.5, stiffness: 170, damping: 2.5, initialVelocity: 0)) {
            raiseTheBell = false // 👈 Release the bell when reached to the highest position
        }
    }
    alarmSet.toggle() // 👈 Not same as the bell animation trigger
    

    Demo

    Login or Signup to reply.
  2. You can use a keyframeAnimator (iOS 17+)

    Image(systemName: "bell.fill")
        .font(.system(size: 250))
        .foregroundColor(.white)
        .keyframeAnimator(initialValue: Angle.zero, trigger: alarmSet) { content, value in
            content.rotationEffect(value, anchor: .top)
        } keyframes: { _ in
            KeyframeTrack {
                let spring = Spring(mass: 0.5, stiffness: 170, damping: 2.5)
                SpringKeyframe(Angle.degrees(10), duration: 0.05, spring: spring)
                // Consider putting this part in a for loop if there are a lot of bounces
                SpringKeyframe(Angle.degrees(-10), duration: 0.1, spring: spring)
                SpringKeyframe(Angle.degrees(10), duration: 0.1, spring: spring)
                SpringKeyframe(Angle.degrees(-10), duration: 0.1, spring: spring)
                SpringKeyframe(Angle.degrees(10), duration: 0.1, spring: spring)
                // the last frame should have start velocity 0 so that it goes back to the initial position
                SpringKeyframe(Angle.zero, duration: 0.05, spring: spring, startVelocity: Angle.zero)
            }
        }
    

    Also consider using some of the built-in springs.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search