探索 SwiftUI 基本手勢
前言
在 SwiftUI 中,我們可以通過添加不同的交互來使我們的應(yīng)用程序更具交互性,這些交互可以響應(yīng)我們的點(diǎn)擊,點(diǎn)擊和滑動。
今天,我們將回顧SwiftUI基本手勢:
- TapGesture
- 長按手勢
- 拖動手勢
- 放大手勢
- 旋轉(zhuǎn)手勢
TapGesture
輕擊手勢使我們能夠識別 View 上的一個或多個輕擊。我們有幾種方法可以添加點(diǎn)擊手勢。
第一個是直接使用 .onTapGesture 修飾符。
- Circle()
- .onTapGesture {
- // Respond to Tap Gesture
- }
SwiftUI 文檔中使用的其他選項是通過創(chuàng)建手勢并將其配置為屬性,然后將其與 .gesture(_:include :) 修飾符一起使用。
注意: 為了執(zhí)行某項操作或響應(yīng)輕擊,我們需要使用 .onEnded 操作關(guān)閉,該操作在手勢結(jié)束時觸發(fā)。
- struct SingleTapGestureView: View {
- var singleTap: some Gesture {
- TapGesture()
- .onEnded { _ in
- // Respond to Tap Gesture
- }
- }
- var body: some View {
- Circle()
- .gesture(singleTap)
- }
- }
實(shí)際上,我更喜歡第二種方法,因為這樣我們可以創(chuàng)建不同的手勢并通過我們的代碼重復(fù)使用它們。
因此,如果我們將代碼放在一起,就可以開始編寫類似的東西。

- struct TapGestureView: View {
- @State private var isAnimating = false
- @State private var tapped1x = 0
- var singleTap: some Gesture {
- TapGesture()
- .onEnded { _ in
- tapped1x += 1
- withAnimation(Animation.easeOut(duration: 0.5)) {
- self.isAnimating = true
- }
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
- self.isAnimating = false
- }
- }
- }
- var body: some View {
- VStack {
- Text("Tapped 1X: \(tapped1x) times")
- .font(.caption)
- Circle()
- .frame(width: 80, height: 80)
- .foregroundColor(.orange)
- .overlay(
- Text("1X")
- .fontWeight(.medium)
- )
- .background(
- Circle()
- .strokeBorder(Color.blue, lineWidth: 3)
- .scaleEffect(isAnimating ? 1.5 : 1)
- .opacity(isAnimating ? 0 : 1)
- )
- .gesture(singleTap)
- }
- }
- }
類似地,我們只需使用 TapGesture(count:Int) 初始化程序就可以控制要響應(yīng)的數(shù)量。
在這種情況下,您需要點(diǎn)擊3次才能觸發(fā) .onEnded 操作關(guān)閉。

- struct TapGesture3xView: View {
- @State private var isAnimating = false
- @State private var tapped3x = 0
- var multipleTap: some Gesture {
- TapGesture(count: 3)
- .onEnded { _ in
- tapped3x += 1
- withAnimation(Animation.easeOut(duration: 0.5)) {
- self.isAnimating = true
- }
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
- self.isAnimating = false
- }
- }
- }
- var body: some View {
- VStack {
- Text("Tapped 3X: \(tapped3x) times")
- .font(.caption)
- Circle()
- .frame(width: 80, height: 80)
- .foregroundColor(.orange)
- .overlay(
- Text("3X")
- .fontWeight(.medium)
- )
- .background(
- Circle()
- .strokeBorder(Color.blue, lineWidth: 3)
- .scaleEffect(isAnimating ? 1.5 : 1)
- .opacity(isAnimating ? 0 : 1)
- )
- .gesture(multipleTap)
- }
- }
- }
長按手勢
長按手勢可讓我們在用戶長按定義的時間后以及在用戶長按的時間內(nèi)執(zhí)行操作。
我們可以設(shè)置一個最小持續(xù)時間,以識別我們的長按手勢??梢栽?LongPressGesture 初始化程序中進(jìn)行設(shè)置。
- LongPressGesture(minimumDuration: 2)
然后,我們可以使用 .updating 方法在長按期間執(zhí)行操作,并使用 .onEnded 在識別到我們的手勢時執(zhí)行操作。
在此示例中,我將在長按操作期間更新 Circle() 的大小和顏色,并且當(dāng)識別出手勢時,我將顯示“文本已完成”。
另外,我在這里使用的是 GestureState 屬性包裝器,該包裝器在長按期間設(shè)置為 true ,在手勢結(jié)束時設(shè)置為 false 。我正在將此屬性包裝器用于示例動畫。

- struct LongPressGestureView: View {
- @GestureState private var isLongPressDetected = false
- @State private var isDone = false
- var longPress: some Gesture {
- LongPressGesture(minimumDuration: 2)
- .updating($isLongPressDetected) { currentState, gestureState, transaction in
- DispatchQueue.main.async {
- isDone = false
- }
- gestureState = currentState
- transaction.animation = Animation.easeIn(duration: 2)
- }
- .onEnded { done in
- isDone = done
- }
- }
- var body: some View {
- VStack {
- Spacer()
- Circle()
- .frame(width: 10, height: 10)
- .foregroundColor(isLongPressDetected ? .orange : .primary)
- .scaleEffect(CGSize(
- width: isLongPressDetected ? 10 : 1,
- height: isLongPressDetected ? 10 : 1))
- Spacer()
- if isLongPressDetected {
- Text("Updating...")
- }
- if isDone {
- Text("Done")
- }
- Spacer()
- Text("Long Press 2 sec")
- .padding()
- .background(isLongPressDetected ? Color.green : Color.orange)
- .cornerRadius(16)
- .gesture(longPress)
- }
- }
- }
拖動手勢
拖動手勢允許我們在拖動視圖時執(zhí)行操作。
我們可以利用并使用 .onChanged 和 .onEnded 關(guān)閉方法來執(zhí)行某些操作。這兩種方法都為我們提供了出色的屬性 DragGesture.Value,該屬性存儲以下拖動動作信息:
location
predictedEndLocation
predictedEndTranslation
startLocation
time
translation
我們可以使用該屬性來創(chuàng)建可移動視圖。在當(dāng)前示例中,我使用 .onChanged 方法更新 Circle() 位置坐標(biāo)。

- struct DragGestureView: View {
- @State private var location: CGPoint = CGPoint(x: 100, y: 100)
- var drag: some Gesture {
- DragGesture(minimumDistance: 1, coordinateSpace: .local)
- .onChanged { value in
- location = value.location
- }
- }
- var body: some View {
- Circle()
- .frame(width: 100, height: 100)
- .foregroundColor(.orange)
- .position(location)
- .gesture(drag)
- }
- }
在這里,添加了 .onEnded 方法,以在拖動結(jié)束后重置 Circle() 位置坐標(biāo)。

- struct DragGestureView: View {
- @State private var location: CGPoint = CGPoint(x: 100, y: 100)
- var drag: some Gesture {
- DragGesture(minimumDistance: 1, coordinateSpace: .local)
- .onChanged { value in
- location = value.location
- }
- .onEnded { value in
- withAnimation(.easeOut) {
- location = CGPoint(x: 100, y: 100)
- }
- }
- }
- var body: some View {
- Circle()
- .frame(width: 100, height: 100)
- .foregroundColor(.orange)
- .position(location)
- .gesture(drag)
- }
- }
放大手勢
當(dāng)我們在View上應(yīng)用放大動作時,放大手勢允許做出一些動作。
在這里,還有 .onChanged 和 .onEnded 閉包,我們可以使用它們來在放大動作期間或結(jié)束時進(jìn)行響應(yīng)。作為屬性,接收到的是 CGFloat 的 MagnificationGesture.Value 。我們可以以此為例來更改視圖大小。

- struct MagnificationGestureView: View {
- @State var magnifiedValue: CGFloat = 1.0
- var magnification: some Gesture {
- MagnificationGesture()
- .onChanged { value in
- magnifiedValue = value
- }
- .onEnded { value in
- magnifiedValue = 1.0
- }
- }
- var body: some View {
- Circle()
- .frame(width: 100 * magnifiedValue, height: 100 * magnifiedValue)
- .foregroundColor(.orange)
- .gesture(magnification)
- .animation(.easeOut)
- }
- }
旋轉(zhuǎn)手勢
旋轉(zhuǎn)手勢允許旋轉(zhuǎn)視圖,并在旋轉(zhuǎn)過程中和旋轉(zhuǎn)結(jié)束時以某些動作做出響應(yīng)。
它還為我們提供了 .onChanged 和 .onEnded 閉包,這些閉包為我們提供了 RotationGesture.Value,它表示手勢 Angle 值。我們可以使用該值旋轉(zhuǎn)視圖。

- struct RotationGestureView: View {
- @State private var angle = Angle(degrees: 0.0)
- @State private var backgroundAngle = Angle(degrees: 0.0)
- var rotation: some Gesture {
- RotationGesture()
- .onChanged { angle in
- self.angle = angle
- }
- .onEnded { angle in
- withAnimation(Animation.spring()) {
- self.backgroundAngle = angle
- }
- }
- }
- var body: some View {
- Rectangle()
- .frame(width: 150, height: 150, alignment: .center)
- .foregroundColor(.orange)
- .rotationEffect(self.angle)
- .gesture(rotation)
- .background(
- Rectangle()
- .shadow(color: .primary, radius: 10, x: 0.0, y: 0.01)
- .foregroundColor(.secondary)
- .rotationEffect(backgroundAngle)
- )
- }
- }
總結(jié)
上面是對 SwiftUI 基本手勢的總結(jié)。我們可以實(shí)現(xiàn)更多的交互使我們的 App 變得更生動。
對于高級的使用,可以將手勢組合或者同時使用以做出響應(yīng),或者可以實(shí)現(xiàn)自己的自定義手勢。