Drag on a Canvas with XCUICoordinate.press(...)

I am trying to create a test case in XCTest using the UI Test target. I have a use case where I use a SwiftUI Canvas to draw a network graph; and the user can drag the graph on the Canvas. After the drag, the user may click on a node. The application presents a popup with the information about the network node clicked. SwiftUI provides the information required for the application to translate the location of the tap into the original location prior to the drag; and so the logic behind the popup can determine which node the network graph was tapped by the user.

It appears that the distance of the drag operation performed does not match distance that the image actually moved. The drag distance is always larger than the actual distance dragged.

I can observe this during the test execution. The arrow starts at the correct spot but then goes further than the object being dragged. I also observe this when I am driving the application.

Is there some standard and supported way to determine the distance that the image on the screen was moved given a specific distance of the "thenDragTo:" coordinate?

Answered by DTS Engineer in 885354022

I built a test project with a SwiftUI Canvas that draws a simple network graph and pans via a DragGesture. I added UI tests that use XCUICoordinate.press(forDuration:thenDragTo:) with various drag distances and compared the requested distance against the content offset the app reported. In every case the values matched 1:1 — a 200-point drag moved the content exactly 200 points.

This suggests the mismatch you're seeing is specific to your view hierarchy or how you apply the drag translation. A few things to check:

  1. Scale transforms: If you apply a scale to the Canvas or a parent view (for example, pinch-to-zoom), the gesture translation arrives in screen points while the content moves in scaled coordinates. A 2x scale would make the content appear to move twice as far as expected.

  2. Coordinate space: If your DragGesture specifies a coordinateSpace other than .local, the translation values are reported in that space, which may not match the Canvas coordinate system.

  3. Nested scroll views or gesture-intercepting containers: A ScrollView or other container with its own gesture handling can add translation on top of what your DragGesture reports.

  4. CGAffineTransform vs offset: If you apply the drag as a transform rather than a direct offset, transforms can compound in ways that amplify movement.

You mentioned you also observe this when driving the application manually, which confirms the issue is in how the drag translation maps to content movement — not specific to XCUITest.

Could you share more about your view hierarchy and how you apply the drag translation to the Canvas content? That would help narrow down the cause.

I built a test project with a SwiftUI Canvas that draws a simple network graph and pans via a DragGesture. I added UI tests that use XCUICoordinate.press(forDuration:thenDragTo:) with various drag distances and compared the requested distance against the content offset the app reported. In every case the values matched 1:1 — a 200-point drag moved the content exactly 200 points.

This suggests the mismatch you're seeing is specific to your view hierarchy or how you apply the drag translation. A few things to check:

  1. Scale transforms: If you apply a scale to the Canvas or a parent view (for example, pinch-to-zoom), the gesture translation arrives in screen points while the content moves in scaled coordinates. A 2x scale would make the content appear to move twice as far as expected.

  2. Coordinate space: If your DragGesture specifies a coordinateSpace other than .local, the translation values are reported in that space, which may not match the Canvas coordinate system.

  3. Nested scroll views or gesture-intercepting containers: A ScrollView or other container with its own gesture handling can add translation on top of what your DragGesture reports.

  4. CGAffineTransform vs offset: If you apply the drag as a transform rather than a direct offset, transforms can compound in ways that amplify movement.

You mentioned you also observe this when driving the application manually, which confirms the issue is in how the drag translation maps to content movement — not specific to XCUITest.

Could you share more about your view hierarchy and how you apply the drag translation to the Canvas content? That would help narrow down the cause.

Thank-you for your reply.

First, I want to re-iterate that the application appears run correctly and the difficulty is only with the XCUITest part. That of course does not mean that the application is put together correctly, only that it seems to work for me.

I did use an explicit co-ordinate space. My SwiftUI Canvas structure employed the .coordinateSpace modifier with a .named(...) parameter specifying a character string constant. My DragGesture also used the same .named(...) parameter; as do my MagnifyGesture and my SpatialTapGesture.

I'll change the code to use .local for the gesture definitions and remove the modifier to the Canvas structure.

It is also the case that my Canvas structure employs an animation modifier .animation(.interactiveSpring, value: currentOffset)

Did you also animate the drag in your test application?

The view hierarchy is:

- NavigationStack
-- VStack
--- Spacer
--- Text
--- HStack
---- Text
---- Text
---- Text
---- Text
--- Text
--- Spacer
--- GeometryReader
---- Canvas
--- NavigationLink

The modifiers used for the Canvas structure are:

  • accessibilityIdentifier
  • coordinateSpace
  • gesture(drag definition)
  • offset(current offset CGSize variable)
  • animation
  • clipped
  • onTapGesture(count of 2 sets currentOffset to .zero and current scale to 1.0)
  • gesture(magnification definition)
  • scaleEffect(scale CGFloat variable initially 1.0 and anchor UnitPoint set to .zero
  • animation(.easeInOut, value: scale)
  • gesture(the SpatialTapGesture)
  • onChangeOf the tap location, sets an optional variable with the display information
  • popover(item: the optional variable with display information)

So, after trying the coordinateSpace change to .local assuming that there is no change), I'll create a simple application and the corresponding UI test classes to explore further.

Thanks again.

Drag on a Canvas with XCUICoordinate.press(...)
 
 
Q