title: SwiftUI Drag gesture in vertical ScrollView that contains horizontal ScrollView
body: <p>I want to implement a <code>Pager</code> that allows a user to move between two views by swiping left or right. Each of these views is a <code>ScrollView</code> that contains many elements (mostly <code>VStacks</code> and <code>HStacks</code> with <code>Text</code> elements, but there are also horizontal <code>ScrollView</code>s containing <code>Text</code> elements).</p>
  <p>I attach a <code>highPriorityGesture(DragGesture())</code> to the Pager and as the user drags I offset the views so that one view becomes visible and the other slides off the screen. In the drag gesture's <code>.onEnded</code> callback if <code>translation.width</code> <code>predicatedTranslation.width</code> exceeds a threshold then I switch the view (so that the user doesn't have to swipe through the whole screen, but only half of it) and if it doesn't exceed the threshold I make the view snap back to its initial position.</p>
  <p>The problem appears when one of the two views embedded in the <code>Pager</code> contains a horizontal <code>ScrollView</code>. Then the <code>highPriorityGesture(DragGesture())</code> misbehaves. What happens is the following:</p>
  <ul>
  <li>the horizontal <code>ScrollView</code> scrolls to the left/right</li>
  <li>the <code>highPriorityGesture(DragGesture())</code> picks up that drag and <code>.onChanged</code> callback fires and updates the offset, but the <code>.onEnded</code> callback never fires</li>
  </ul>
  <p>The result is that the <code>Pager</code> moves one view a bit to the left/right, but since the <code>.onEnded</code> callback doesn't fire it just leave that view there instead of making it move back to the initial position (so it might end in showing 95% of the first view and 5% of the second view).</p>
  <p>I also tried using <code>.gesture(DragGesture())</code> instead of <code>highPriorityGesture(DragGesture())</code>, but it still picks up the drag from the child <code>ScrollView</code>.</p>
  <p>Is there any way of making the child horizontal <code>ScrollView</code> to exclusively handle the its drag?</p>
  <p>EDIT:</p>
  <p>It happens also if one of the two views is simply a vertical <code>ScrollView</code> without any nested horizontal <code>ScrollViews</code>. Somehow even though it is vertical it can be dragged diagonally.</p>
  <pre><code>struct Pager2View&lt;T1: View, T2: View&gt;: View {
  
  let pageCount: Int
  let pageOne: T1
  let pageTwo: T2
  @Binding var currentIndex: Int
  @State private var translation: CGSize = .zero
  @State private var offset: CGFloat = 0.0
  
  func exceedsThreshold(gesture: DragGesture.Value, geometryWidth: CGFloat) -&gt; Bool {
      (abs(gesture.translation.width) &gt; (geometryWidth / 2)) || (abs(gesture.predictedEndTranslation.width) &gt; (geometryWidth / 2))
  }
  
  var body: some View {
      GeometryReader { geometry in
          HStack {
              self.pageOne.frame(width: geometry.size.width)
              self.pageTwo.frame(width: geometry.size.width)
          }
          .frame(width: geometry.size.width, alignment: .leading)
          .offset(x: self.offset)
          .onChange(of: currentIndex) { value in
              withAnimation {
                  if currentIndex == 0 {
                      self.translation.width = 0
                  } else {
                      self.translation.width = -geometry.size.width
                  }
              }
          }
          .onChange(of: translation) { value in
              withAnimation {
                  self.offset = max(-geometry.size.width, min(0, -geometry.size.width * CGFloat(currentIndex) + self.translation.width))
              }
          }
          .highPriorityGesture(
              DragGesture()
                  .onChanged { gesture in
                      self.translation = gesture.translation              
                  }
                  .onEnded { gesture in
                      
                      if exceedsThreshold(gesture: gesture, geometryWidth: geometry.size.width) {
                          if gesture.translation.width &lt; 0 &amp;&amp; exceedsThreshold(gesture: gesture, geometryWidth: geometry.size.width) &amp;&amp; currentIndex == 0 {
                              withAnimation {
                                  self.translation.width = 0
                                  currentIndex = min(self.pageCount - 1, currentIndex + 1)
                                  
                              }
                          } else if gesture.translation.width &gt; 0 &amp;&amp; exceedsThreshold(gesture: gesture, geometryWidth: geometry.size.width) &amp;&amp; currentIndex == 1 {
                              withAnimation {
                                  self.translation.width = geometry.size.width
                                  currentIndex = max(0, currentIndex - 1)
                              }
                          }
                      }  else {
                          withAnimation(.interactiveSpring()) {
                              if currentIndex == 0 {
                                  self.translation.width = 0
                              } else {
                                  self.translation.width = -geometry.size.width
                              }
                          }
                      }
                  }
          )
      }
  }
  }
  </code></pre>