探索 SwiftUI 基本手勢

前言

SwiftUI 中,我們可以通過添加不同的交互來使我們的應用程序更具交互性,這些交互可以響應我們的點擊,點擊和滑動。

今天,我們將回顧 SwiftUI 基本手勢:

  • TapGesture
  • 長按手勢
  • 拖動手勢
  • 放大手勢
  • 旋轉手勢

TapGesture

輕擊手勢使我們能夠識別 View 上的一個或多個輕擊。
我們有幾種方法可以添加點擊手勢。

第一個是直接使用 .onTapGesture 修飾符。

Circle()
  .onTapGesture {
    // Respond to Tap Gesture 
  }

SwiftUI 文檔中使用的其他選項是通過創建手勢並將其配置爲屬性,然後將其與 .gesture(_:include :) 修飾符一起使用。

注意: 爲了執行某項操作或響應輕擊,我們需要使用 .onEnded 操作關閉,該操作在手勢結束時觸發。

struct SingleTapGestureView: View {
  var singleTap: some Gesture {
      TapGesture()
          .onEnded { _ in
              // Respond to Tap Gesture
          }
  }

  var body: some View {
      Circle()
          .gesture(singleTap)
  }
}

實際上,我更喜歡第二種方法,因爲這樣我們可以創建不同的手勢並通過我們的代碼重複使用它們。

因此,如果我們將代碼放在一起,就可以開始編寫類似的東西。

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) 初始化程序就可以控制要響應的數量。

在這種情況下,您需要點擊3次才能觸發 .onEnded 操作關閉。

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)
        }
    }
}

長按手勢

長按手勢可讓我們在用戶長按定義的時間後以及在用戶長按的時間內執行操作。

我們可以設置一個最小持續時間,以識別我們的長按手勢。 可以在 LongPressGesture 初始化程序中進行設置。

LongPressGesture(minimumDuration: 2)

然後,我們可以使用 .updating 方法在長按期間執行操作,並使用 .onEnded 在識別到我們的手勢時執行操作。

在此示例中,我將在長按操作期間更新 Circle() 的大小和顏色,並且當識別出手勢時,我將顯示“文本已完成”。

另外,我在這裏使用的是 GestureState 屬性包裝器,該包裝器在長按期間設置爲 true ,在手勢結束時設置爲 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)
        }
    }
}

拖動手勢

拖動手勢允許我們在拖動視圖時執行操作。

我們可以利用並使用 .onChanged.onEnded 關閉方法來執行某些操作。 這兩種方法都爲我們提供了出色的屬性 DragGesture.Value,該屬性存儲以下拖動動作信息:

  • location
  • predictedEndLocation
  • predictedEndTranslation
  • startLocation
  • time
  • translation

我們可以使用該屬性來創建可移動視圖。 在當前示例中,我使用 .onChanged 方法更新 Circle() 位置座標。

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 方法,以在拖動結束後重置 Circle() 位置座標。

<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/55a7965269b746aa8c0d40538c70f3fc~tplv-k3u1fbpfcp-zoom-1.image">

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)
    }
}

放大手勢

當我們在View上應用放大動作時,放大手勢允許做出一些動作。

在這裏,還有 .onChanged.onEnded 閉包,我們可以使用它們來在放大動作期間或結束時進行響應。 作爲屬性,接收到的是 CGFloatMagnificationGesture.Value 。 我們可以以此爲例來更改視圖大小。

<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/858b930cd72345efa695cffd55ce949a~tplv-k3u1fbpfcp-zoom-1.image">

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)
    }
}

旋轉手勢

旋轉手勢允許旋轉視圖,並在旋轉過程中和旋轉結束時以某些動作做出響應。

它還爲我們提供了 .onChanged.onEnded 閉包,這些閉包爲我們提供了 RotationGesture.Value,它表示手勢 Angle 值。 我們可以使用該值旋轉視圖。

<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/16b29b23bd084056a28e1c8ba611b3dc~tplv-k3u1fbpfcp-zoom-1.image">

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)
            )
    }
}

總結

上面是對 SwiftUI 基本手勢的總結。我們可以實現更多的交互使我們的 App 變得更生動。

對於高級的使用,可以將手勢組合或者同時使用以做出響應,或者可以實現自己的自定義手勢。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章