ios – Animation issue in SwiftUI


Here’s what I have so far based for an alarm view animation that I’m working on

struct AlarmView: View {
    @State var alarmSet = false
    @State var animateBell = false
    @State var alarmText = "Remind Me"
    @State var shouldAnimate = false
    @State private var bellID: UUID = UUID()
    @State private var timeSinceLastTap = Date.now
    
    var body: some View {
        ZStack {
            Color.black
            VStack {
                Button(action: {
                    // animate only if there is some time elapsed between taps
                    let duration = min(0.3, abs(timeSinceLastTap.timeIntervalSinceNow))
                    shouldAnimate = duration > 0.2
                    alarmText = !alarmSet ? "Reminder Set" : "Remind Me"

                    if shouldAnimate {
                        if alarmSet == true { // turn OFF alarm
                            alarmSet.toggle()
                        } else { // animate the bell, then turn ON alarm
                            animateBell.toggle()
                            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                                alarmSet.toggle()
                            }
                        }
                    } else {
                        // to cancel animation
                        bellID = UUID()
                        alarmSet.toggle()
                    }
                    timeSinceLastTap = Date.now
                    
                }, label: {
                    VStack {
                        VStack {
                            if alarmSet {
                                Image(systemName: "checkmark")
                                    .font(.system(size: 150))
                                    .foregroundColor(.white)
                            } else {
                                Image(systemName: "bell.fill")
                                    .font(.system(size: 150))
                                    .foregroundColor(.white)
                                    .id(bellID)
                                    .keyframeAnimator(initialValue: Angle.zero, trigger: animateBell) { 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)
                                            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)
                                            SpringKeyframe(Angle.zero, duration: 0.05, spring: spring, startVelocity: Angle.zero)
                                        }
                                    }
                            }
                        }
                        .frame(width: 150, height: 150)
                        Text(alarmText)
                            .font(.largeTitle)
                            .foregroundStyle(.gray)
                    }
                })
                .buttonStyle(NoAnimationStyle())
            }
        }
        .ignoresSafeArea()
    }
}

struct NoAnimationStyle: PrimitiveButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .contentShape(Rectangle())
            .onTapGesture(perform: configuration.trigger)
    }
}

This works well for the most part except for the following scenario. When tapping the button while in its default state (with the bell showing and text saying “Remind me”), if I tap again while the bell is animating/wiggling it momentarily shows the checkmark before showing the bell again.

However the text continues to say “Reminder Set” instead of defaulting to “Remind Me”. The effect I’m trying to replicate is the reminder animation in the Netflix app in their ‘New & Hot’ screen. From what I checked, the Netflix animation is buggy on iOS where quick taps cause it to animate horribly even after the taps have ended. On Android however, the animation is perfect wherein if user taps twice quickly, just the text changes while the icon stays unchanged and does not switch at all between quick taps.

In my case ideally I’d like for the bell animation to cancel even if mid animation & not show the checkmark at all and for the text to be set appropriately. How can I achieve this? Any help is appreciated.

Latest articles

spot_imgspot_img

Related articles

Leave a reply

Please enter your comment!
Please enter your name here

spot_imgspot_img