Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)

like image 988
JohnSF Avatar asked Jan 12 '20 19:01

JohnSF


People also ask

How to overlay an image in SwiftUI?

As mentioned earlier, SwiftUI has a built-in modifier for applying overlay named .overlay that can be attached to the existing image view. The requirement inside an overlay modifier is to place another object that will serve as the coating of the image.

How to add a header to an image in SwiftUI?

Add a header title above the image and position it towards the top of the screen: Position the image within a custom frame: Note that I’m using the SwiftUI canvas live preview of my code and it highlighted the border of my custom frame because I kept the blinking cursor on the Image code.

How to add image to Xcode project in SwiftUI?

In order to add it to your Xcode project, download it to your computer, click on Assets.xcassets in Xcode project and drag and drop the image onto the list of assets. Follow this introductory tutorial on how to add image to Xcode project in SwiftUI.

How do I magnify a Tesla image in Swift?

A basic familiarity with Swift. The most popular magnification gesture can be found in photos, where you can magnify a photo by scaling it larger or smaller. Now, you will attempt to magnify a Tesla image. You will need a GestureState to store the value. The n, you will need to create an image that will also take the scale value.


2 Answers

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()
like image 151
Gent Avatar answered Oct 12 '22 10:10

Gent


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)
like image 29
JohnSF Avatar answered Oct 12 '22 10:10

JohnSF