Creating an Instagram-Like Zoom Effect in SwiftUI

April 20, 2023

If you’ve ever used Instagram, you’ve likely encountered the pinch-to-zoom effect used to enlarge images. This effect allows users to zoom in on details and get a closer look at photos.

In this post, let’s have a look on how to create a similar zoom effect in SwiftUI.

Before getting started, if you need to check out the Instagram’s feature we are trying to replicate, you can check out a sample video below:

https://bqtojrlgmcjymtitneuf.supabase.co/storage/v1/object/public/storage/instagram-sample.gif?t=2023-06-04T06%3A10%3A15.016Z

Creating a new SwiftUI project and adding an image asset:

You should be already familiar with the process of creating a new SwiftUI project and importing image assets if you are learning about gestures in SwiftUI.

A quick guide to creating a new SwiftUI project is given below.

  • Open Xcode and create a new SwiftUI project.
  • Select App as the project template under iOS section and click on Next.
  • Give your project a name and click on Next. Make sure to select SwiftUI as the user interface.
  • Save the project in a location of your choice and click on Create.

Creating a new SwiftUI project

  • For an image as a placeholder, import any image of your choice by dragging and dropping the image file into the assets folder of your project in the project navigator of Xcode.

In my project, I have used an image of a car as a placeholder

(Credits: Erik Mclean on Unsplash).

Add a new Image view to the ContentView struct

Inside the ContentView struct present in the template, create a new Image view.

Set the image to be resizable and scaled to fit the available space.

struct ContentView: View {
    var body: some View {
        Image("car")
            .resizable()
            .scaledToFit()
    }
}

Add a scaleEffect modifier to theImage view

Apply a scaleEffect modifier to the Image view, which will be used to adjust the scale of the image based on the user’s pinch gesture. Also, create a new private variable scale of type CGFloat and set it to 1.0 so as to set the initial scale of the image to 1.0 and track the scale of the image as the user performs the pinch gesture and store the changes in here.

struct ContentView: View {
    @State private var scale: CGFloat = 1.0

    var body: some View {
        Image("car")
            .resizable()
            .scaledToFit()
            .scaleEffect(scale)
    }
}

Adding a MagnificationGesture to the Image view

Attach a MagnificationGesture to the Image view using the gesture modifier. When the user changes the magnification value, update the scale property using the onChanged closure.

struct ContentView: View {
    @State private var scale: CGFloat = 1.0

    var body: some View {
        Image("car")
            .resizable()
            .scaledToFit()
            .scaleEffect(scale)
            .gesture(MagnificationGesture()
                        .onChanged { value in
                            self.scale = value.magnitude
                        }
            )
    }
}

Add an onEnded closure to the MagnificationGesture

Add an onEnded closure to the MagnificationGesture that sets the scale property back to its original value of 1.0 when the user releases the pinch gesture. This will cause the image view to return to its original size and position.

struct ContentView: View {
    @State private var scale: CGFloat = 1.0

    var body: some View {
        Image("car")
            .resizable()
            .scaledToFit()
            .scaleEffect(scale)
            .gesture(MagnificationGesture()
                        .onChanged { value in
                            self.scale = value.magnitude
                        }
                        .onEnded { _ in
                            self.scale = 1.0
                        }
            )
    }
}

Add a conditional statement to the onChanged closure

Add a conditional statement to the onChanged closure of the MagnificationGesture that checks if the magnification value is less than 1.0. If it is, set the scale property to 1.0, which will cause the image view to return to its original size. If the magnification value is greater than or equal to 1.0, set the scale property to the magnitude of the pinch gesture, as before.

This step is crucial because if the user performs a pinch gesture that is less than 1.0, the image view will shrink to a size smaller than the original image. This is not the desired behavior, so we need to add a conditional statement to prevent this from happening.

struct ContentView: View {
    @State private var scale: CGFloat = 1.0

    var body: some View {
        Image("car")
            .resizable()
            .scaledToFit()
            .scaleEffect(scale)
            .gesture(MagnificationGesture()
                        .onChanged { value in
                            if value.magnitude < 1.0 {
                                self.scale = 1.0
                            } else {
                                self.scale = value.magnitude
                            }
                        }
                        .onEnded { _ in
                            self.scale = 1.0
                        }
            )
    }
}

Some final touches:

Wrap the Image in a VStack and add a padding modifier to the VStack to add some space between the edges of the screen and the image view.

struct ContentView: View {
    @State private var scale: CGFloat = 1.0

    var body: some View {
        VStack {
            Image("car")
                .resizable()
                .scaledToFit()
                .scaleEffect(scale)
                .gesture(MagnificationGesture()
                            .onChanged { value in
                                if value.magnitude < 1.0 {
                                    self.scale = 1.0
                                } else {
                                    self.scale = value.magnitude
                                }
                            }
                            .onEnded { _ in
                                self.scale = 1.0
                            }
                )
        }
        .padding()
    }
}

Final result

https://bqtojrlgmcjymtitneuf.supabase.co/storage/v1/object/public/storage/final-result.gif?t=2023-06-04T06%3A10%3A44.333Z

Conclusion

Great, you’ve just created an Instagram-like zoom effect in SwiftUI. We’ve learnt about creating this using the MagnificationGesture and scaleEffect modifiers.

With just a few lines of code, you have added interactivity and a touch of elegance to your SwiftUI app.

Try experimenting with different images and gestures to create your own unique effects.

I hope you found this tutorial helpful.

Thank you for reading this far.