1.1 用手势添加交互性

原文链接: https://developer.apple.com/documentation/swiftui/adding-interactivity-with-gestures

1.1 用手势添加交互性

使用手势修饰符为应用添加交互性。

概述

手势修饰符会处理处理触摸等用户输入事件所需的全部逻辑,并识别这些事件何时匹配某个已知的手势模式,例如长按或旋转。当识别到某个模式时,SwiftUI 会运行你提供的回调,用来更新视图状态或执行某个动作。

为视图添加手势修饰符

你添加的每个手势都作用于视图层级中某个特定的视图。要识别某个特定视图上的手势事件,请先创建并配置该手势,然后使用 gesture(_:including:) 修饰符:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
struct ShapeTapView: View {
    var body: some View {
        let tap = TapGesture()
            .onEnded { _ in
                print("View tapped!")
            }
        
        return Circle()
            .fill(Color.blue)
            .frame(width: 100, height: 100, alignment: .center)
            .gesture(tap)
    }
}

响应手势回调

根据你添加到手势修饰符上的回调,SwiftUI 会在手势状态变化时随时向你的代码报告。手势修饰符提供三种接收更新的方式:updating(_:body:)、onChanged(_:) 和 onEnded(_:)。

更新临时的界面状态

要在手势变化时更新视图,请为视图添加一个 GestureState 属性,并在 updating(_:body:) 回调中更新它。SwiftUI 一旦识别到该手势就会调用 updating 回调,并在手势值每次变化时再次调用。例如,SwiftUI 会在放大手势一开始就调用 updating 回调,之后每当放大倍数变化时再次调用。当用户结束或取消手势时,SwiftUI 不会调用 updating 回调。相反,这个手势状态属性会自动把状态重置回初始值。

举例来说,要制作一个在用户执行长按时改变颜色的视图,请添加一个手势状态属性,并在 updating 回调中更新它。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
struct CounterView: View {
    @GestureState private var isDetectingLongPress = false
    
    var body: some View {
        let press = LongPressGesture(minimumDuration: 1)
            .updating($isDetectingLongPress) { currentState, gestureState, transaction in
                gestureState = currentState
            }
        
        return Circle()
            .fill(isDetectingLongPress ? Color.yellow : Color.green)
            .frame(width: 100, height: 100, alignment: .center)
            .gesture(press)
    }
}

在手势过程中更新持久状态

要跟踪那些在手势结束后不应重置的手势变化,请使用 onChanged(_:) 回调。例如,要统计应用识别到长按的次数,请添加一个 onChanged(_:) 回调并递增一个计数器。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct CounterView: View {
    @GestureState private var isDetectingLongPress = false
    @State private var totalNumberOfTaps = 0
    
    var body: some View {
        let press = LongPressGesture(minimumDuration: 1)
            .updating($isDetectingLongPress) { currentState, gestureState, transaction in
                gestureState = currentState
            }.onChanged { _ in
                self.totalNumberOfTaps += 1
            }
        
        return VStack {
            Text("\(totalNumberOfTaps)")
                .font(.largeTitle)
            
            Circle()
                .fill(isDetectingLongPress ? Color.yellow : Color.green)
                .frame(width: 100, height: 100, alignment: .center)
                .gesture(press)
        }
    }
}

手势结束时更新持久状态

要识别手势何时成功完成并获取该手势的最终值,请使用 onEnded(_:) 函数在回调中更新应用的状态。SwiftUI 只会在手势成功时调用 onEnded(_:) 回调。例如,在 LongPressGesture 过程中,如果用户在 minimumDuration 秒过去之前停止触摸视图,或者手指移动超过 maximumDistance 点,SwiftUI 就不会调用 onEnded(_:) 回调。

例如,要在用户完成一次长按之后停止统计长按尝试次数,请添加一个 onEnded(_:) 回调,并有条件地应用该手势修饰符。

 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
struct CounterView: View {
    @GestureState private var isDetectingLongPress = false
    @State private var totalNumberOfTaps = 0
    @State private var doneCounting = false
    
    var body: some View {
        let press = LongPressGesture(minimumDuration: 1)
            .updating($isDetectingLongPress) { currentState, gestureState, transaction in
                gestureState = currentState
            }.onChanged { _ in
                self.totalNumberOfTaps += 1
            }
            .onEnded { _ in
                self.doneCounting = true
            }
        
        return VStack {
            Text("\(totalNumberOfTaps)")
                .font(.largeTitle)
            
            Circle()
                .fill(doneCounting ? Color.red : isDetectingLongPress ? Color.yellow : Color.green)
                .frame(width: 100, height: 100, alignment: .center)
                .gesture(doneCounting ? nil : press)
        }
    }
}