在 SwiftUI 中检测 DragGesture 取消
Detect DragGesture cancelation in SwiftUI
所以我有一个添加了 DragGesture 的矩形,我想跟踪手势的开始、变化和结束。问题是当我在执行手势时将另一根手指放在 Rectangle 上时,第一个手势停止调用 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)"
}
)
感谢@krjw 手指数偶数的提示
这似乎是 Gesture 框架中的一个问题,它试图检测一堆手势,即使我们没有指定它应该监听它们。
由于文档非常稀疏,我们只能真正猜测这里的预期行为和生命周期是什么(恕我直言 - 这似乎是一个错误) - 但它可以解决。
定义一个类似
的结构方法
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:)
所以我有一个添加了 DragGesture 的矩形,我想跟踪手势的开始、变化和结束。问题是当我在执行手势时将另一根手指放在 Rectangle 上时,第一个手势停止调用 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)"
}
)
感谢@krjw 手指数偶数的提示
这似乎是 Gesture 框架中的一个问题,它试图检测一堆手势,即使我们没有指定它应该监听它们。
由于文档非常稀疏,我们只能真正猜测这里的预期行为和生命周期是什么(恕我直言 - 这似乎是一个错误) - 但它可以解决。
定义一个类似
的结构方法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:)