PK-V
PK-V

Reputation: 214

Animate scaleEffect modifier between original to higher size

I have an imageview in SwiftUI and i want to scale the view to higher size for fraction of second and return to it's normal size. How can i do this in SwiftUI?


Image("disagreeSmall")
.scaleEffect(scale)
.animation(.easeIn(duration: 0.15), value: selectedRating)

Upvotes: -1

Views: 523

Answers (1)

Benzy Neez
Benzy Neez

Reputation: 21730

If you are able to use functionality that was only introduced in iOS 17 then there are two easy ways to achieve this animation:

1. Use a .symbolEffect

For SF symbols, the .bounce effect scales up and then down again:

Image(systemName: "hand.thumbsdown.fill")
    .symbolEffect(.bounce, value: selectedRating)

2. Use a PhaseAnimator

A PhaseAnimator can be used to perform chained animations:

PhaseAnimator([false, true], trigger: selectedRating) { phase in
    Image("disagreeSmall")
        .scaleEffect(phase ? 1.1 : 1.0)
} animation: { phase in
    phase ? .easeIn(duration: 0.15) : .easeOut(duration: 0.15)
}

If you need to support older iOS versions then you could use other mechanisms to chain the animations. This is how it could be done using the animation completion callback outlined in the answer to SwiftUI withAnimation completion callback:

// Credit to Centurion for the AnimatableModifier solution:
// https://stackoverflow.com/a/62855108/20386264
struct AnimationCompletionCallbackCGFloat: ViewModifier, Animatable {

    var targetValue: CGFloat
    var completion: () -> ()

    init(animatedValue: CGFloat, completion: @escaping () -> ()) {
        self.targetValue = animatedValue
        self.completion = completion
        self.animatableData = animatedValue
    }

    var animatableData: CGFloat {
        didSet {
            checkIfFinished()
        }
    }

    func checkIfFinished() -> () {
        if (animatableData == targetValue) {
            DispatchQueue.main.async {
                self.completion()
            }
        }
    }

    func body(content: Content) -> some View {
        content
    }
}
Image("disagreeSmall")
    .scaleEffect(scale)
    .onChange(of: selectedRating) { newVal in // pre iOS 17
        withAnimation(.easeIn(duration: 0.15)) {
            scale = 1.1
        }
    }
    .modifier(
        AnimationCompletionCallbackCGFloat(animatedValue: scale) {
            withAnimation(.easeOut(duration: 0.15)) {
                scale = 1.0
            }
        }
    )

Upvotes: 0

Related Questions