Обнаружение отмены 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:)