前言
在 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
閉包,我們可以使用它們來在放大動作期間或結束時進行響應。 作爲屬性,接收到的是 CGFloat
的 MagnificationGesture.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 變得更生動。
對於高級的使用,可以將手勢組合或者同時使用以做出響應,或者可以實現自己的自定義手勢。