Обнаружение отмены DragGesture в SwiftUI

Итак, у меня есть Rectangle с добавленным DragGesture, и я хочу отслеживать начало, изменение и окончание жеста. Проблема в том, что когда я кладу другой палец на прямоугольник во время выполнения жеста, первый жест перестает вызывать обработчик onChange и не запускает обработчик onEnded. Также обработчики не стреляют по второму пальцу.

Но если я поставлю третий палец, не удаляя два предыдущих, обработчики этого жеста начнут срабатывать (и так далее с четными нажатиями, отменяющие нечетные)

Это ошибка? Есть ли способ определить, что первый жест был отменен?

Rectangle()
  .fill(Color.purple)
  .gesture(
    DragGesture(minimumDistance: 0, coordinateSpace: .local)
      .onChanged() { event in
        self.debugLabelText = "changed \(event)"
      }
      .onEnded() { event in
        self.debugLabelText = "ended \(event)"
      }
  )

2 ответа

Решение

Спасибо @krjw за подсказку с четным количеством пальцев

Похоже, это проблема в структуре жестов для попытки обнаружить набор жестов, даже если мы не указали, что он должен их прослушивать.

Поскольку документация крайне скудна, мы можем только догадываться о предполагаемом поведении и жизненном цикле здесь (IMHO - это похоже на ошибку), но это можно обойти.

Определите метод структуры, например

func onDragEnded() {
  // set state, process the last drag position we saw, etc
}

Затем объедините несколько жестов в один, чтобы охватить основы, которые мы не указали.

let drag = DragGesture(minimumDistance: 0)
      .onChanged({ drag in
       // Do stuff with the drag - maybe record what the value is in case things get lost later on 
      })
      .onEnded({ drag in
        self.onDragEnded()
      })

     let hackyPinch = MagnificationGesture(minimumScaleDelta: 0.0)
      .onChanged({ delta in
        self.onDragEnded()
      })
      .onEnded({ delta in
        self.onDragEnded()
      })

    let hackyRotation = RotationGesture(minimumAngleDelta: Angle(degrees: 0.0))
      .onChanged({ delta in
        self.onDragEnded()
      })
      .onEnded({ delta in
        self.onDragEnded()
      })

    let hackyPress = LongPressGesture(minimumDuration: 0.0, maximumDistance: 0.0)
      .onChanged({ _ in
        self.onDragEnded()
      })
      .onEnded({ delta in
        self.onDragEnded()
      })

    let combinedGesture = drag
      .simultaneously(with: hackyPinch)      
      .simultaneously(with: hackyRotation)
      .exclusively(before: hackyPress)

/// The pinch and rotation may not be needed - in my case I don't but 
///   obviously this might be very dependent on what you want to achieve

Может быть лучшая комбинация для simultaneously а также exclusively но, по крайней мере, для моего варианта использования (который похож на джойстик), похоже, он выполняет свою работу

Также есть GestureMask тип, который мог бы выполнить работу, но нет документации о том, как это работает.

Одно из решений - использовать @GestureStateсвойство, которое отслеживает, выполняется ли перетаскивание в данный момент. После отмены жеста состояние будет автоматически сброшено на false.

      struct DragSampleView: View {
    @GestureState private var dragGestureActive: Bool = false
    @State var dragOffset: CGSize = .zero

    var draggingView: some View {
        Text("DRAG ME").padding(50).background(.red)
    }

    var body: some View {
        ZStack {
            Color.blue.ignoresSafeArea()
            draggingView
                .offset(dragOffset)
                .gesture(DragGesture()
                    .updating($dragGestureActive) { value, state, transaction in
                        state = true
                    }
                    .onChanged { value in
                        print("onChanged")
                        dragOffset = value.translation
                    }.onEnded { value in
                        print("onEnded")
                        dragOffset = .zero
                    })
                .onChange(of: dragGestureActive) { newIsActiveValue in
                    if newIsActiveValue == false {
                        dragCancelled()
                    }
                }
        }
    }

    private func dragCancelled() {
        print("dragCancelled")
        dragOffset = .zero
    }
}

struct DragV_PreviewProvider: PreviewProvider {
    static var previews: some View {
        DragSampleView()
    }
}

См. Https://developer.apple.com/documentation/swiftui/draggesture/updating(_:body:)

Другие вопросы по тегам