Simultaneous Drag and Magnification Gestures

I wrote an application that enables simultaneous rotate and magnification gestures on a rectange. This application worked as expected. When I adapted the same application to enable simultaneous drag and magnification gestures on a rectange, I can either drag the rectangle or resize the rectangle, but I cannot do both.


I'm thinking this behavior is normal, because the two gestures are different; that is, drag is a one-finger gesture and magnify is a two-finger gesture. Can someone confirm this behavior for me?


If this is indeed working as expected, then how can I drag and resize a view at the same time?

Accepted Reply

Assuming the platform is iOS...


See the HIGs https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/gestures/


>but I cannot do both.


Yeah, pick one. Apple trains users to expect consistent UI behavior, and even if you found a way to make this work, you risk rejection during review, so...

Replies

Assuming the platform is iOS...


See the HIGs https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/gestures/


>but I cannot do both.


Yeah, pick one. Apple trains users to expect consistent UI behavior, and even if you found a way to make this work, you risk rejection during review, so...

Apple's own maps app has simultaneous drag and magnification

I sort of couldn't believe this was an actual issue, but it seems to be impossible to create a pure SwiftUI view that pans and zooms at the same time!

This is one of the core multitouch behaviors which Apple has supported in Photos, Maps and Safari for fourteen years. Every Apple app which supports zooming an image also supports simultaneous panning.

I filed this serious UI bug as FB9488452 with the following sample code:

struct ContentView: View {
	@GestureState var zoom = CGFloat(1.0)
	@GestureState var pan = CGSize.zero

    var body: some View {
		VStack {
			Image(systemName: "person")
				.font(.largeTitle)
				.padding()
				.foregroundColor(.black)
		}.frame(maxWidth: .infinity, maxHeight: .infinity)
		.background(Color.white)
		.scaleEffect(zoom)
		.offset(pan)
		.animation(.spring())
		.gesture(
			MagnificationGesture().updating($zoom){ (value, state, transaction) in
				state = value
			}.simultaneously(with: DragGesture())
				.updating($pan){ (value, state, transaction) in
					state = value.second?.translation ?? .zero
					print("Translation: \(value.second?.translation ?? .zero)")
				}
		)
	}
}

The above code should compose a gesture which allows simultaneous dragging and zooming, but only one of the gestures will succeed. It's as if they have been specified as Exclusive rather than Simultaneous. This is clearly a bug, as these gestures should absolutely be able to compose.

Any update on this issue ? I also assumed combining 2 drag gestures to pan with 2 fingers not just one should be as easy as this:

var move: some Gesture {
    return DragGesture().simultaneously(with: DragGesture())
      .updating($offset){ value, state, transaction in
          // compute state based on value.first and value.second
    }
  }

However, this gesture only gets triggered when the user drags with 1 finger instead of 2. Another issue is that the pinch example from HIGs https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/gestures/ shows how the zoom is done depending on where the user pinches not always in the center of the image. This isn't currently possible to implement by only using MagnificationGesture as it only provides a CGFloat and no information of the offset we should apply when zooming. Currently I don't see a way for implementing pinch to zoom like in the Photos app using only SwiftUI, please correct me if I'm wrong.

  • You're not wrong! Please file a bug—I believe teams prioritize based on number of duplicate bugs.

Add a Comment