简体   繁体   中英

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:

在此处输入图片说明

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

在此处输入图片说明

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

To make it simpler and more readable, I created an extension/modifier for that

struct DraggableView: ViewModifier {
    @State var offset = CGPoint(x: 0, y: 0)
    
    func body(content: Content) -> some View {
        content
            .gesture(DragGesture(minimumDistance: 0)
                .onChanged { value in
                    self.offset.x += value.location.x - value.startLocation.x
                    self.offset.y += value.location.y - value.startLocation.y
            })
            .offset(x: offset.x, y: offset.y)
    }
}

extension View {
    func draggable() -> some View {
        return modifier(DraggableView())
    }
}

Now all you have to do is call the modifier:

Image(systemName: "plus")
   .draggable()

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)

I'm a little late, but for anyone looking, please try this as it worked for me. What you want to do is change the offset of the image right after you zoom out in your MagnificationGesture.onChanged() lifecycle.

var magnification: some Gesture {
   MagnificationGesture()
       .updating($magnifyBy) {
        .....
        }
       .onChanged() { _ in
            //Zoom Out value of magnifyBy < 1.0
            if self.magnifyBy <= 0.6{
               //Change offset of image after zoom out to center of screen
                self.currentPosition = CGSize(width: 1.0, height: 1.0)
           }
           self.newPosition = self.currentPosition
       }
       .onEnded{
        .....
       } }

var body: some View{
      Image()
        .offset(x: self.currentPosition.width, y: self.currentPosition.height)}

Anyone questions please let me know

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM