1.2 组合 SwiftUI 手势
原文链接: https://developer.apple.com/documentation/swiftui/composing-swiftui-gestures
1.2 组合 SwiftUI 手势
组合手势以创建复杂的交互。
概述
当你为应用的视图层级添加多个手势时,需要决定这些手势如何相互作用。你可以用组合手势来定义 SwiftUI 识别手势的顺序。手势组合有三种类型:
- 同时(Simultaneous)
- 顺序(Sequenced)
- 互斥(Exclusive)
当你同时组合多个手势修饰符时,SwiftUI 必须同时识别出所有子手势模式,才会认为识别到了这个组合手势。当你把一个手势修饰符接在另一个之后时,SwiftUI 必须按顺序识别出每个子手势。最后,当你以互斥方式组合手势时,只要 SwiftUI 只识别出其中一个子手势而没有识别出其他子手势,就会认为识别到了整个手势模式。
让一个手势接在另一个手势之后
当你让一个手势接在另一个手势之后时,SwiftUI 会先识别第一个手势,再识别第二个手势。例如,为了要求用户先长按才能拖拽某个视图,你可以让 DragGesture 接在 LongPressGesture 之后。
为顺序手势的状态建模
为了更容易跟踪复杂的状态,请使用一个枚举来涵盖配置视图所需的所有状态。在下面的示例中有三个重要状态:没有交互(inactive)、长按进行中(pressing)以及拖拽中(dragging)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| struct DraggableCircle: View {
enum DragState {
case inactive
case pressing
case dragging(translation: CGSize)
var translation: CGSize {
switch self {
case .inactive, .pressing:
return .zero
case .dragging(let translation):
return translation
}
}
var isActive: Bool {
switch self {
case .inactive:
return false
case .pressing, .dragging:
return true
}
}
var isDragging: Bool {
switch self {
case .inactive, .pressing:
return false
case .dragging:
return true
}
}
}
@GestureState private var dragState = DragState.inactive
@State private var viewState = CGSize.zero
...
|
创建手势并更新界面状态
当你把两个手势按顺序组合时,各个回调会同时捕获两个手势的状态。在 update 回调中,新的 value 使用一个枚举来表示各种可能手势状态的组合。下面的代码把底层手势状态转换为前面定义的高层 DragState 枚举。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| ...
var body: some View {
let minimumLongPressDuration = 0.5
let longPressDrag = LongPressGesture(minimumDuration: minimumLongPressDuration)
.sequenced(before: DragGesture())
.updating($dragState) { value, state, transaction in
switch value {
// 长按开始。
case .first(true):
state = .pressing
// 长按确认,可以开始拖拽。
case .second(true, let drag):
state = .dragging(translation: drag?.translation ?? .zero)
// 拖拽结束,或长按被取消。
default:
state = .inactive
}
}
.onEnded { value in
guard case .second(true, let drag?) = value else { return }
self.viewState.width += drag.translation.width
self.viewState.height += drag.translation.height
}
...
|
当用户开始按住视图时,拖拽状态变为 pressing,形状下方会出现带动画的阴影。当用户完成长按、拖拽状态变为 dragging 之后,形状周围会出现一个边框,表示用户可以开始移动这个视图了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| ...
Circle()
.fill(Color.blue)
.overlay(dragState.isDragging ? Circle().stroke(Color.white, lineWidth: 2) : nil)
.animation(.linear(duration: minimumLongPressDuration), value: dragState.isDragging)
.frame(width: 100, height: 100, alignment: .center)
.offset(
x: viewState.width + dragState.translation.width,
y: viewState.height + dragState.translation.height
)
.shadow(radius: dragState.isActive ? 8 : 0)
.animation(.linear(duration: minimumLongPressDuration), value: dragState.isActive)
.gesture(longPressDrag)
}
}
|