Making a zoomable Image, in SwiftUI

This should have been simple, but it wasn't. So if you too are having a hard time making a zoomable image in SwiftUI, come on in!

Making a zoomable Image, in SwiftUI
Photo by Tobias Reich / Unsplash

Welcome to this tutorial on creating a zoomable image in SwiftUI! If you've been struggling with various options for creating a zoomable image in SwiftUI, you're not alone.

While there are many solutions, some require tons of boilerplate, and others only work in UIKit. Some, finaly, simply work, until they don't run on UIKit. Others work, until they don't.

Having tried those, and looked into a few libraries (I especially liked this one, until I realized it would only stay zoomed in as long as you were actually pinching) , I can say JarWarren and Haolong were right. (Un)surprisingly, turning our image into a PDF document and displaying it using PDFKit is the simplest way I've found so far to make a zoomable Image you can pan around in SwiftUI (using UIViewRepresentable).

For those of you struggling with it, here's my take on it (careful, it seems PDFKit has it's own weird quirks):

struct ZoomableImage: UIViewRepresentable {

        // used to set the image that will be displayed in the PDFView
        private(set) var image: UIImage
        
        // sets the background color of the PDFView
        private(set) var backgroundColor: Color
        
        // sets the minimum scale factor for zooming out of the image
        private(set) var minScaleFactor: CGFloat
 
        // sets the ideal scale factor for the image when it is first displayed in the PDFView
        // the initial zoom level of the image when it is first displayed
        private(set) var idealScaleFactor: CGFloat
        
        // sets the maximum scale factor for zooming in on the image
        private(set) var maxScaleFactor: CGFloat

        public init(
            image: UIImage,
            backgroundColor: Color,
            minScaleFactor: CGFloat,
            idealScaleFactor: CGFloat,
            maxScaleFactor: CGFloat
        ) {
            self.image = image
            self.backgroundColor = backgroundColor
            self.minScaleFactor = minScaleFactor
            self.idealScaleFactor = idealScaleFactor
            self.maxScaleFactor = maxScaleFactor
        }

        public func makeUIView(context: Context) -> PDFView {
            let view = PDFView()
            guard let page = PDFPage(image: image) else { return view }
            let document = PDFDocument()
            document.insert(page, at: 0)

            view.backgroundColor = UIColor(cgColor: backgroundColor.cgColor!)

            view.autoScales = true
            view.document = document

            view.maxScaleFactor = maxScaleFactor
            view.minScaleFactor = minScaleFactor
            view.scaleFactor = idealScaleFactor
            return view
        }

        public func updateUIView(_ uiView: PDFView, context: Context) {
            // empty
        }
 }

It is important to note that the idealScaleFactor should be between the minScaleFactor and maxScaleFactor. If not, the idealScaleFactor will be set to the closest of  minScaleFactor or maxScaleFactor.

Conclusion

In conclusion, creating a zoomable image in SwiftUI can be a bit tricky, but using PDFKit to turn the image into a PDF document and display it using a PDFView is a ridiculous yet simple and effective solution.Thanks for reading, and have a great day!