Image Scale Animation in SwiftUI

DevTechie Inc
Jun 24

SwiftUI Gestures are fun and easy to implement adding animation to them is as easy as adding a gesture to the views.

In this article we will look at applying MagnificationGesture in SwiftUI with animation 😃

Here is what our end product will look like:

We will start with a GestureState variable to capture scale while magnification gesture is being applied and a State variable to store the endScale value. Both will be CGFloat type.

@GestureState private var scale: CGFloat = 0.0
@State private var endScale: CGFloat = 1.0
Let’s add an Image view with name of the image stored in our Assets catalog

struct ImageScaleAnimation: View {
    
    @GestureState private var scale: CGFloat = 0.0
    @State private var endScale: CGFloat = 1.0
    
    var body: some View {
        Image("imageSample")
    }
}
Image is really big so it will be all zoomed and only portion of the image will appear on screen:

We will make the image fit to the screen with scaledToFit modifier but before we apply that modifier, we will have to make image resizable:

struct ImageScaleAnimation: View {
    
    @GestureState private var scale: CGFloat = 0.0
    @State private var endScale: CGFloat = 1.0
    
    var body: some View {
        Image("imageSample")
            .resizable()
            .scaledToFit()
    }
}
Next, we will apply scaleEffect, which will be addition of scale value and endScale value.

struct ImageScaleAnimation: View {
    
    @GestureState private var scale: CGFloat = 0.0
    @State private var endScale: CGFloat = 1.0
    
    var body: some View {
        Image("imageSample")
            .resizable()
            .scaledToFit()
            .scaleEffect(scale + endScale)
    }
}
Now is the time to add gesture to our Image view. We will apply MagnificationGesture and when the gesture is updating we will get scale value as one of the value in closure, so using that value, we will update GestureState as shown below

struct ImageScaleAnimation: View {
    
    @GestureState private var scale: CGFloat = 0.0
    @State private var endScale: CGFloat = 1.0
    
    var body: some View {
        Image("imageSample")
            .resizable()
            .scaledToFit()
            .scaleEffect(scale + endScale)
            .gesture(
                MagnificationGesture()
                    .updating($scale, body: { value, state, transaction in
                        state = value.magnitude
                    })
                    .onEnded({ value in
                        endScale += value.magnitude
                    })
            )
    }
}
We can make zoom in and out with an animation modifier:

struct ImageScaleAnimation: View {
    
    @GestureState private var scale: CGFloat = 0.0
    @State private var endScale: CGFloat = 1.0
    
    var body: some View {
        Image("imageSample")
            .resizable()
            .scaledToFit()
            .scaleEffect(scale + endScale)
            .gesture(
                MagnificationGesture()
                    .updating($scale, body: { value, state, transaction in
                        state = value.magnitude
                    })
                    .onEnded({ value in
                        endScale += value.magnitude
                    })
            )
            .animation(.easeOut, value: endScale)
    }
}
Magnification Gesture on simulator get’s stuck some times so we will add a tapGesture to reset endScale value and because we want this change to take part in animation as well, we will add this gesture right before animation modifier:

struct ImageScaleAnimation: View {
    
    @GestureState private var scale: CGFloat = 0.0
    @State private var endScale: CGFloat = 1.0
    
    var body: some View {
        Image("imageSample")
            .resizable()
            .scaledToFit()
            .scaleEffect(scale + endScale)
            .gesture(
                MagnificationGesture()
                    .updating($scale, body: { value, state, transaction in
                        state = value.magnitude
                    })
                    .onEnded({ value in
                        endScale += value.magnitude
                    })
            )
            .onTapGesture {
                endScale = 1.0
            }
            .animation(.easeOut, value: endScale)
    }
}



With that we have reached the end of this article. Thank you once again for reading. Subscribe to our weekly newsletter at https://www.devtechie.com