5

Trying to compose 3 simultaneous gestures in SwiftUI, yet SwiftUI's SimultaneousGesture() only works with 2 gestures.

For example I have a basic View such as:

struct myRect: View {
    var body: some View {
        Rectangle()
            .fill(Color.red)
            .frame(width: 200, height: 200)
    }
}

And I need this view to be draggable, rotatable, and resizable.

I'm assuming I would need to use DragGesture(), RotationGesture(), and MagnificationGesture(). But I'm stuck since SimultaneousGesture() only takes in 2 gestures max. How can I get these 3 gestures to work on my View?

Edit:

I should specify that these gestures have to work simultaneously. This is exactly like the question asked here: Pinch, Pan, and Rotate Text Simultaneously like Snapchat [SWIFT 3]

However that was for UIKit. Is it possible to do this in SwiftUI?

user12533955
  • 489
  • 8
  • 20

2 Answers2

0

Your view myRect may have clash with other views. I test SimultaneousGesture() modifier, it works fine having three gestures by any sequence like blow:

struct myRect: View {
let rg = RotationGesture().onChanged({ a in
    print("Angle = \(a.degrees)")
})
let dg = DragGesture(minimumDistance: 1, coordinateSpace: .local).onChanged({ v in
    print("Translate = \(v.translation.width)")
})
let mg = MagnificationGesture().onChanged({ s in
    print("scale = \(s)")
})
var body: some View {
    Rectangle()
        .fill(Color.red)
        .frame(width: 200, height: 200)
        .simultaneousGesture(rg)
        .simultaneousGesture(mg)
        .simultaneousGesture(dg)
}
}
Mir
  • 411
  • 1
  • 7
  • 16
  • 1
    Unfortunately these don't work together simultaneously. With this implementation I would be able to resize and rotate at the same time, but not resize, rotate, and also drag. The functionality I'm after is the exact same as: https://stackoverflow.com/q/45402639/12533955 – user12533955 Apr 10 '20 at 06:10
  • @user12533955, Please specify your issue, which one gesture doesn't give output ? – Mir Apr 10 '20 at 08:21
  • With your implementation dragging does not give an output while a user is magnifying and rotating the View. I'm looking to have them all work simultaneously. So a user can magnify & drag at the same time, rotate & drag at the same time, and magnify & rotate & drag at the same time. Check out the stackoverflow post I linked, they explain the exact functionality I'm after – user12533955 Apr 10 '20 at 17:44
0

@Mir’s answer was absolutely correct. But you could also do it the following way.

As you will see, all gestures fire simultaneously.

The real problem here is the SwiftUI ‘design’ choice that I would rather consider a bug.

Just try to touch the same view a second time while you are dragging and your first gesture will stop updating and even leave you dangling without an .onEnded callback. The new (second) gesture does not fire any callback at all, it’s non-existent and just kills your first gesture!!! So no way to establish a decent state machine, sorry :-)

Yet another (third) touchDown spins up a correctly functioning new drag gesture, totally ignorant of the first two attempts, even if you didn’t release the finger(s) from gesture one and two, and they are still on the screen ;-)

That is the reason why 2 finger gestures like rotation and magnification don’t fire simultaneously with 1 finger drag gestures because the first subsequent touch after a (single finger) drag kills the whole drag gesture.

It has nothing to do with the rotation/magnification gestures per se…

_

struct ContentView: View {
@GestureState var state: DragGesture.Value?

var body: some View {
    
    let drag3 = DragGesture(minimumDistance: 0, coordinateSpace: .global)
        .onChanged { gesture in print("DRAG 3 - onChanged \(gesture.startLocation)") }
        .onEnded { gesture in print("DRAG 3 - onEnded \(gesture.startLocation)") }
        .updating($state) { value, _, _ in print("DRAG 3 - updating \(value.startLocation)") }
    
    let drag2 = DragGesture(minimumDistance: 0, coordinateSpace: .global)
        .onChanged { gesture in print("DRAG 2 - onChanged \(gesture.startLocation)") }
        .onEnded { gesture in print("DRAG 2 - onEnded \(gesture.startLocation)")}
        .updating($state) { value, _, _ in print("DRAG 2 - updating \(value.startLocation)")}
        .simultaneously(with: drag3)
    
    let drag1 = DragGesture(minimumDistance: 0, coordinateSpace: .global)
        .onChanged { gesture in print("DRAG 1 - onChanged \(gesture.startLocation)") }
        .onEnded { gesture in print("DRAG 1 - onEnded \(gesture.startLocation)") }
        .updating($state) { value, _, _ in print("DRAG 1 - updating \(value.startLocation)") }
        .simultaneously(with: drag2)
    
    
    HStack() {
        Rectangle()
            .fill(Color.red)
            .frame(width: 200, height: 200)
            .gesture(drag1)
        //.simultaneousGesture(drag1)
        //.simultaneousGesture(drag2)
        //.simultaneousGesture(drag3)
    }
}

}

mramosch
  • 458
  • 4
  • 14