Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI: pinch to zoom on image

Tags:

I want to allow the user to pinch-to-zoom in on an Image in SwiftUI. I figured the best way to go was to use a MagnificationGesture and, following along with the answer here, I ended up with this code:

// outside of `var body: some View`
@State private var scale: Int = 1.0
@State private var lastScale: Int = 1.0

// Image 
Image("dog")
.resizable()
.aspectRatio(contentMode: .fit)
.gesture(MagnificationGesture()
    .onChanged { val in
        let delta = val / self.lastScale
        self.lastScale = val
        let newScale = self.scale * delta
        self.scale = newScale
    }
    .onEnded { _ in
        self.lastScale = 1.0
    }
)
.scaleEffect(scale)

This code handles magnification fine, but does not let the user zoom in on a specific area. Instead, it always zooms in on the middle of the image.

How would I go about handling pinch-to-zoom behavior on an image in SwiftUI?

Thanks in advance!

like image 708
ntrupin Avatar asked Dec 21 '19 14:12

ntrupin


2 Answers

The code creates a pinch-to-zoom effect by adding a drag gesture in addition to the magnification gesture. Use of viewState allows a changing offset position when using the drag gesture.

struct ContentView: View {

    @State private var scale: CGFloat = 1.0
    @State private var lastScale: CGFloat = 1.0
    @State private var viewState = CGSize.zero

    var body: some View {

    Image("dog")
        .resizable()
        .aspectRatio(contentMode: .fit)
        .animation(.spring())
        .offset(x: viewState.width, y: viewState.height)
        .gesture(DragGesture()
            .onChanged { val in
                self.viewState = val.translation
            }
        )
        .gesture(MagnificationGesture()
            .onChanged { val in
                let delta = val / self.lastScale
                self.lastScale = val
                if delta > 0.94 { // if statement to minimize jitter
                    let newScale = self.scale * delta
                    self.scale = newScale
                }
            }
            .onEnded { _ in
                self.lastScale = 1.0
            }
        )
        .scaleEffect(scale)
        }
}

The 'if' statement was added to minimize the jitter caused by frequent updates. Nothing is special about the 0.94 value, just set by trial and error.

The .animation(spring()) statement was added for a more natural-looking dragging effect.

like image 152
Marcy Avatar answered Oct 05 '22 01:10

Marcy


I found that the easiest way to achieve is to use PDFKit provided by Apple .

1.Start by creating PDFView

    import SwiftUI
    import PDFKit

    struct PhotoDetailView: UIViewRepresentable {
    let image: UIImage
    func makeUIView(context: Context) -> PDFView {
        let view = PDFView()
        view.document = PDFDocument()
        guard let page = PDFPage(image: image) else { return view }
        view.document?.insert(page, at: 0)
        view.autoScales = true
        view.backgroundColor = .clear
        return view
    }
    
    func updateUIView(_ uiView: PDFView, context: Context) {
        
    }
    }

2.Use in swiftUI view, like this

TabView(selection: $index,
                content:  {
                //this line
                PhotoDetailView(image: images[index])
                    .offset(imageViewerOffset)
                
        })
        .tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
like image 38
Haolong Avatar answered Oct 05 '22 00:10

Haolong