如何使用 SwiftUI 在视图上检测向上、向下、向左和向右滑动
How to detect Swiping UP, DOWN, LEFT and RIGHT with SwiftUI on a View
我开始构建 Apple Watch 应用程序。
我目前正在做的工作需要我在四个主要方向(UP
、DOWN
、LEFT
和 RIGHT
上使用检测滑动)
问题是我不知道如何检测到这一点。我一直在环顾四周,我已经走到死胡同了。
当用户在视图上滑动 UP
时,我可以对下面的视图做些什么来打印 swiped up
?
struct MyView: View {
var body: some View {
Text("Hello, World!")
}
}
谢谢。
你可以使用 DragGesture
.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .local)
.onEnded({ value in
if value.translation.width < 0 {
// left
}
if value.translation.width > 0 {
// right
}
if value.translation.height < 0 {
// up
}
if value.translation.height > 0 {
// down
}
}))
如果你想要一个对滑动方向性更“宽容”的,你可以使用更多的条件来帮助平衡它:
编辑:做了一些更多的测试,显然第二个条件的值增加了一些混乱,所以我调整了它们以消除上述混乱并使手势防弹(拖动到角落现在会出现“没有线索”而不是其中一种手势)...
let detectDirectionalDrags = DragGesture(minimumDistance: 3.0, coordinateSpace: .local)
.onEnded { value in
print(value.translation)
if value.translation.width < 0 && value.translation.height > -30 && value.translation.height < 30 {
print("left swipe")
}
else if value.translation.width > 0 && value.translation.height > -30 && value.translation.height < 30 {
print("right swipe")
}
else if value.translation.height < 0 && value.translation.width < 100 && value.translation.width > -100 {
print("up swipe")
}
else if value.translation.height > 0 && value.translation.width < 100 && value.translation.width > -100 {
print("down swipe")
}
else {
print("no clue")
}
由于其他解决方案在物理设备上有点不一致,我决定想出另一种似乎在不同屏幕尺寸上更加一致的解决方案,因为除了 [=11= 之外没有硬编码值].
.gesture(DragGesture(minimumDistance: 20, coordinateSpace: .global)
.onEnded { value in
let horizontalAmount = value.translation.width as CGFloat
let verticalAmount = value.translation.height as CGFloat
if abs(horizontalAmount) > abs(verticalAmount) {
print(horizontalAmount < 0 ? "left swipe" : "right swipe")
} else {
print(verticalAmount < 0 ? "up swipe" : "down swipe")
}
})
根据本杰明的回答,这是一种更快捷的处理方式
.gesture(DragGesture(minimumDistance: 3.0, coordinateSpace: .local)
.onEnded { value in
print(value.translation)
switch(value.translation.width, value.translation.height) {
case (...0, -30...30): print("left swipe")
case (0..., -30...30): print("right swipe")
case (-100...100, ...0): print("up swipe")
case (-100...100, 0...): print("down swipe")
default: print("no clue")
}
}
)
这个响应更快:
.gesture(DragGesture(minimumDistance: 3.0, coordinateSpace: .local)
.onEnded { value in
let direction = atan2(value.translation.width, value.translation.height)
switch direction {
case (-Double.pi/4..<Double.pi/4): self.playMove(.down)
case (Double.pi/4..<Double.pi*3/4): self.playMove(.right)
case (Double.pi*3/4...Double.pi), (-Double.pi..<(-Double.pi*3/4)):
self.playMove(.up)
case (-Double.pi*3/4..<(-Double.pi/4)): self.playMove(.left)
default:
print("unknown)")
}
}
为了简单起见,我会创建一个修饰符。用法将如下所示:
yourView
.onSwiped(.down) {
// Action for down swipe
}
或
yourView
.onSwiped { direction in
// React to detected swipe direction
}
您还可以使用 trigger
参数来配置接收更新:连续或仅在手势结束时。
完整代码如下:
struct SwipeModifier: ViewModifier {
enum Directions: Int {
case up, down, left, right
}
enum Trigger {
case onChanged, onEnded
}
var trigger: Trigger
var handler: ((Directions) -> Void)?
func body(content: Content) -> some View {
content.gesture(
DragGesture(
minimumDistance: 24,
coordinateSpace: .local
)
.onChanged {
if trigger == .onChanged {
handle([=12=])
}
}.onEnded {
if trigger == .onEnded {
handle([=12=])
}
}
)
}
private func handle(_ value: _ChangedGesture<DragGesture>.Value) {
let hDelta = value.translation.width as CGFloat
let vDelta = value.translation.height as CGFloat
if abs(hDelta) > abs(vDelta) {
handler?(hDelta < 0 ? .left : .right)
} else {
handler?(vDelta < 0 ? .up : .down)
}
}
}
extension View {
func onSwiped(
trigger: SwipeModifier.Trigger = .onChanged,
action: @escaping (SwipeModifier.Directions) -> Void
) -> some View {
let swipeModifier = SwipeModifier(trigger: trigger) {
action([=12=])
}
return self.modifier(swipeModifier)
}
func onSwiped(
_ direction: SwipeModifier.Directions,
trigger: SwipeModifier.Trigger = .onChanged,
action: @escaping () -> Void
) -> some View {
let swipeModifier = SwipeModifier(trigger: trigger) {
if direction == [=12=] {
action()
}
}
return self.modifier(swipeModifier)
}
}
我开始构建 Apple Watch 应用程序。
我目前正在做的工作需要我在四个主要方向(UP
、DOWN
、LEFT
和 RIGHT
上使用检测滑动)
问题是我不知道如何检测到这一点。我一直在环顾四周,我已经走到死胡同了。
当用户在视图上滑动 UP
时,我可以对下面的视图做些什么来打印 swiped up
?
struct MyView: View {
var body: some View {
Text("Hello, World!")
}
}
谢谢。
你可以使用 DragGesture
.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .local)
.onEnded({ value in
if value.translation.width < 0 {
// left
}
if value.translation.width > 0 {
// right
}
if value.translation.height < 0 {
// up
}
if value.translation.height > 0 {
// down
}
}))
如果你想要一个对滑动方向性更“宽容”的,你可以使用更多的条件来帮助平衡它:
编辑:做了一些更多的测试,显然第二个条件的值增加了一些混乱,所以我调整了它们以消除上述混乱并使手势防弹(拖动到角落现在会出现“没有线索”而不是其中一种手势)...
let detectDirectionalDrags = DragGesture(minimumDistance: 3.0, coordinateSpace: .local)
.onEnded { value in
print(value.translation)
if value.translation.width < 0 && value.translation.height > -30 && value.translation.height < 30 {
print("left swipe")
}
else if value.translation.width > 0 && value.translation.height > -30 && value.translation.height < 30 {
print("right swipe")
}
else if value.translation.height < 0 && value.translation.width < 100 && value.translation.width > -100 {
print("up swipe")
}
else if value.translation.height > 0 && value.translation.width < 100 && value.translation.width > -100 {
print("down swipe")
}
else {
print("no clue")
}
由于其他解决方案在物理设备上有点不一致,我决定想出另一种似乎在不同屏幕尺寸上更加一致的解决方案,因为除了 [=11= 之外没有硬编码值].
.gesture(DragGesture(minimumDistance: 20, coordinateSpace: .global)
.onEnded { value in
let horizontalAmount = value.translation.width as CGFloat
let verticalAmount = value.translation.height as CGFloat
if abs(horizontalAmount) > abs(verticalAmount) {
print(horizontalAmount < 0 ? "left swipe" : "right swipe")
} else {
print(verticalAmount < 0 ? "up swipe" : "down swipe")
}
})
根据本杰明的回答,这是一种更快捷的处理方式
.gesture(DragGesture(minimumDistance: 3.0, coordinateSpace: .local)
.onEnded { value in
print(value.translation)
switch(value.translation.width, value.translation.height) {
case (...0, -30...30): print("left swipe")
case (0..., -30...30): print("right swipe")
case (-100...100, ...0): print("up swipe")
case (-100...100, 0...): print("down swipe")
default: print("no clue")
}
}
)
这个响应更快:
.gesture(DragGesture(minimumDistance: 3.0, coordinateSpace: .local)
.onEnded { value in
let direction = atan2(value.translation.width, value.translation.height)
switch direction {
case (-Double.pi/4..<Double.pi/4): self.playMove(.down)
case (Double.pi/4..<Double.pi*3/4): self.playMove(.right)
case (Double.pi*3/4...Double.pi), (-Double.pi..<(-Double.pi*3/4)):
self.playMove(.up)
case (-Double.pi*3/4..<(-Double.pi/4)): self.playMove(.left)
default:
print("unknown)")
}
}
为了简单起见,我会创建一个修饰符。用法将如下所示:
yourView
.onSwiped(.down) {
// Action for down swipe
}
或
yourView
.onSwiped { direction in
// React to detected swipe direction
}
您还可以使用 trigger
参数来配置接收更新:连续或仅在手势结束时。
完整代码如下:
struct SwipeModifier: ViewModifier {
enum Directions: Int {
case up, down, left, right
}
enum Trigger {
case onChanged, onEnded
}
var trigger: Trigger
var handler: ((Directions) -> Void)?
func body(content: Content) -> some View {
content.gesture(
DragGesture(
minimumDistance: 24,
coordinateSpace: .local
)
.onChanged {
if trigger == .onChanged {
handle([=12=])
}
}.onEnded {
if trigger == .onEnded {
handle([=12=])
}
}
)
}
private func handle(_ value: _ChangedGesture<DragGesture>.Value) {
let hDelta = value.translation.width as CGFloat
let vDelta = value.translation.height as CGFloat
if abs(hDelta) > abs(vDelta) {
handler?(hDelta < 0 ? .left : .right)
} else {
handler?(vDelta < 0 ? .up : .down)
}
}
}
extension View {
func onSwiped(
trigger: SwipeModifier.Trigger = .onChanged,
action: @escaping (SwipeModifier.Directions) -> Void
) -> some View {
let swipeModifier = SwipeModifier(trigger: trigger) {
action([=12=])
}
return self.modifier(swipeModifier)
}
func onSwiped(
_ direction: SwipeModifier.Directions,
trigger: SwipeModifier.Trigger = .onChanged,
action: @escaping () -> Void
) -> some View {
let swipeModifier = SwipeModifier(trigger: trigger) {
if direction == [=12=] {
action()
}
}
return self.modifier(swipeModifier)
}
}