Search code examples
iosswiftuigesture

SwiftUI Can't PanGesture an Image


I have not been able to find an equivalent to the pan gesture in SwiftUI. I do see and use magnify, tap, drag and rotate - but I do not see any built in pan. In the following code snippet I add an image and allow the user to zoom - but I want the user to also move the zoomed image to focus on the area of interest. Dragging, of course does not do the job - it just moves the frame.

I tried layering a frame on top and moving the bottom image but could not make that work either.

 struct ContentView: View {

    @State var scale: CGFloat = 1.0
    @State var isScaled: Bool = false
    @State private var dragOffset = CGSize.zero

    var body: some View {
        GeometryReader { geo in
            VStack {
                ZStack{
                    RoundedRectangle(cornerRadius: 40)
                        .foregroundColor(Color.white)
                        .frame(width: geo.size.width - 45, height: geo.size.width - 45)
                        .shadow(radius: 10)

                    Image("HuckALaHuckMedium")
                        .resizable()
                        .scaleEffect(self.scale)
                        .frame(width: geo.size.width - 60, height: geo.size.width - 60)
                        .cornerRadius(40)
                        .aspectRatio(contentMode: .fill)
                        .shadow(radius: 10, x: 20, y: 20)

                        //need pan not drag
                        .gesture(
                            DragGesture()
                                .onChanged { self.dragOffset = $0.translation }
                                .onEnded { _ in self.dragOffset = .zero }
                        )
                        //this works but you can't "zoom twice"
                        .gesture(MagnificationGesture()
                            .onChanged { value in
                                self.scale = self.isScaled ? 1.0 : value.magnitude
                        }
                        .onEnded({ value in
                            //self.scale = 1.0
                            self.isScaled.toggle()
                        })
                        )
                        .animation(.easeInOut)
                        .offset(self.dragOffset)
                }//zstack
                Spacer()
            }
        }
    }
}

An original image example:

enter image description here

And that image after zoom - but with drag not pan:

enter image description here

Any guidance would be appreciated. Xcode 11.3 (11C29)


Solution

  • For others. This is really simple. Ensure that the magnification is accomplished before the drag is allowed - it will work like a pan. First define the drag gesture:

        let dragGesture = DragGesture()
            .onChanged { (value) in
                self.translation = value.translation
            }
            .onEnded { (value) in
                self.viewState.width += value.translation.width
                self.viewState.height += value.translation.height
                self.translation = .zero
            }
    

    Then create an @State value that you toggle to true after the magnification gesture has been activated. When you attach the gesture to the image, do so conditionally:

     .gesture(self.canBeDragged ? dragGesture : nil)