SwiftUI 手势操作
基本概念
在ios中我們經常用到的手勢操作有點擊,長按,拖拽,縮放,旋轉。在手勢操作時,不同的手勢有不同的觸發函數,比如,點擊在點擊完成時觸發某個函數,拖拽手勢不僅僅在拖拽完成時觸發函數,而在拖拽的過程中也可以觸發兩個函數,在這兩個函數中捕捉到不同的參數。對于手勢的捕獲也有兩種方式,我們具體的看下這兩種方式
方式一,通過gesture捕獲手勢
我們可以給View添加一個gesture()的裝飾函數來捕捉手勢,在這個函數中設定不同Gesture()就可以捕捉到不同的手勢。在捕捉到手勢之后,還有3個回調函數處理不同的情況,updating(_:body)在手勢操作時被調用,這個函數需要綁定一個@GestureState修飾的變量,這個變量在回調時被修改,但只是臨時的修改,手勢操作完這個變量會被修改為手勢操作之前的初始化值.onChange(_:)這個同樣是在手勢操作時被調用,這個函數中修改的變量在手勢操作結束時被保留。onEnd()這個函數時在手勢操作成功之后調用。
縮放
我們設定一個圖片縮放場景來演示縮放手勢,先通過image顯示一張圖片,我們通過縮放手勢讓圖片放大或者縮小。捕獲的gesture是MagnificationGesture.代碼如下
@GestureState var zoom:CGFloat=1Image("j1").resizable().frame(width: 200, height: 200, alignment: .leading).scaleEffect(zoom).gesture(MagnificationGesture().updating($zoom, body: {gestureZoom,z,transaction inz=gestureZoom}))設定一個@GestureState修飾的變量zoom,用來控制圖片的縮放比例。在這段代碼中gestureZoom是我們做縮放手勢時的縮放比例,z就是zoom,我們將gestureZoom的數值賦值過去,圖片就放大/縮小,當我們手勢結束時,zoom就變成1。圖片回到原先大小。
@State var zoom:CGFloat=1 Image("j1").resizable().frame(width: 200, height: 200, alignment: .leading).scaleEffect(zoom).gesture(MagnificationGesture().onChanged({z in zoom=z}))這段代碼,我們將zoom用@State修飾,onChange中z是縮放比例,我們將z賦值給zoom。圖片同樣放大/縮小.但是手勢結束時,圖片不會還原。
@State var zoom:CGFloat=1 Image("j1").resizable().frame(width: 200, height: 200, alignment: .leading).scaleEffect(zoom).gesture(MagnificationGesture().onEnded({z in zoom=z}))這段代碼,我們在onEnd中設置zoom的數值,圖片只有在手勢完成之后才顯示出來縮放。
以上的三段代碼展示了三個回調函數的作用。那么我們要完成一個圖片的縮放功能是如何實現的呢。代碼如下
,假設上次手勢是放大了3倍,而這次手勢也是放大操作,但只放大了2倍,那么圖片就會由放大3倍變成放大2倍,實際變小了,我們期望是圖片繼續放大的,這樣的結果不是我們期望的。所以,我們根據手勢的操作來設置放大比例,手勢只要是放大操作,那么放大比例就增加,保證圖片是放大的。
拖拽
我們設定場景是,通過拖拽讓圖片在不同的位置顯示。捕獲的Gesture是DragGesture.代碼如下
@GestureState var dragOffset=CGSize.zeroImage("j1").resizable().frame(width: 200, height: 200, alignment: .leading).offset(x: dragOffset.width, y: dragOffset.height).gesture(DragGesture().updating($dragOffset, body: { gesture,dragoff,transaction indragoff.width=gesture.translation.widthdragoff.height=gesture.translation.height}))我們先在updating處理,這個需要綁定的是一個CGSize類型的屬性。通過這個屬性的width和height來設置圖片的偏移位置,在拖拽結束時,圖片會到原先的位置。
@State var offsetX:CGFloat=0 @State var offsetY:CGFloat=0 Image("j1").resizable().frame(width: 200, height: 200, alignment: .leading).offset(x: offsetX, y: offsetY).gesture(DragGesture().onEnded({v inoffsetX=v.translation.widthoffsetY=v.translation.height}))這段代碼是在手勢操作結束捕獲。圖片會移動到手指抬起的位置。手指移動時沒有任何變化。
@State var offsetX:CGFloat=0 @State var offsetY:CGFloat=0 let step:CGFloat=3Image("j1").resizable().frame(width: 200, height: 200, alignment: .leading).offset(x: offsetX, y: offsetY).gesture(DragGesture().onChanged({gesture inoffsetX+=gesture.translation.width>0 ? step : -stepoffsetY+=gesture.translation.height>0 ? step: -step}))這段代碼,我們用手按住圖片進行滑動,圖片就隨著手移動,實現了圖片的拖拽功能。手勢結束時圖片還是在結束的位置。其中gesture可以認為是一個手勢操作的封裝對象,我們獲取了這個對象中的位移相關的屬性。通過判斷位移的位置設定圖片偏移量x,y的數值。同樣為了讓圖片保持連續移動,我們沒有直接將手勢結束時的位置賦值給圖片,而是判斷出方向再將X,Y的偏移量進行增加或者減少。
旋轉
通過旋轉手勢讓圖片旋轉,同樣有3個回調函數
@GestureState var gestureAngle:Angle=Angle(degrees: 0) Image("j1").resizable().frame(width: 200, height: 200, alignment: .leading).rotationEffect(gestureAngle).gesture(RotationGesture().updating($gestureAngle, body: {angle,ganlge,transaction inganlge=angle}))手勢結束后,圖片會還原。
@State var angle:Angle=Angle(degrees: 0) Image("j1").resizable().frame(width: 200, height: 200, alignment: .leading).rotationEffect(angle).gesture(RotationGesture().onChanged({ a in angle=a}))手勢結束后,圖片保留旋轉狀態
@State var angle:Angle=Angle(degrees: 0) Image("j1").resizable().frame(width: 200, height: 200, alignment: .leading).rotationEffect(angle).gesture(RotationGesture().onEnded({a in angle=a}))只有在手勢結束之后圖片才會旋轉并保持旋轉狀態
點擊
點擊手勢只有2個回調函數 updating和onEnd,在gesture時通過count:Int的構造設定需要連續點擊的觸發次數。
@GestureState var tap:CGFloat=0 Image("j1").resizable().frame(width: 200, height: 200, alignment: .leading).rotationEffect(angle).gesture(TapGesture().updating($tap, body: { d,t,trasaction inprint("d \(d)")print("tap \(t)")}))沒有任何反應
Image("j1").resizable().frame(width: 200, height: 200, alignment: .leading).rotationEffect(angle).gesture(TapGesture(count: 2).onEnded({print("tap over")}))連續點擊2次會打印tap over
長按
長按手勢也有三個回調函數
@GestureState var longPress=falseImage("j1").resizable().frame(width: 200, height: 200, alignment: .leading).rotationEffect(angle).gesture(LongPressGesture().updating($longPress, body: {b ,state,transaction inprint("b \(b)")print("state \(state)")}))手指放上去就會觸發,b就為true。
Image("j1").resizable().frame(width: 200, height: 200, alignment: .leading).rotationEffect(angle).gesture(LongPressGesture().onChanged({b in print(b)}))同updating手指放上去就會觸發
Image("j1").resizable().frame(width: 200, height: 200, alignment: .leading).rotationEffect(angle).gesture(LongPressGesture(minimumDuration: 5, maximumDistance: 5).onEnded({b in print(b)}))onEnd回調函數能體現長按是否被正確觸發,在這段代碼中構造函數添加了2個參數minimumDuration按住的時間maximumDistance按住時最大的移動距離,按住的時間要大于設定的minimumDuration并且手指移動的距離要小于maximumDistance才會真正觸發。如果沒有滿足上面2個條件,不會打印b。
方式二
SwiftUI對于點擊和長按這兩個手勢提供了幾個簡單的修飾函數。
點擊
對于點擊手勢有三個函數onTapGesture(perform: <() -> Void),onTapGesture{code},onTapGesture(count: Int, perform: () -> Void)都是在點擊手勢完成時調用,閉包函數時調用時的代碼。具體使用方式可以對比方式一的函數。
長按
長按手勢的處理有兩個函數onLongPressGesture(perform: () -> Void),onLongPressGesture(minimumDuration: Double, maximumDistance: CGFloat, perform: () -> Void, onPressingChanged: ((Bool) -> Void)?),可以對比方式一的函數寫代碼。
總結
總結
以上是生活随笔為你收集整理的SwiftUI 手势操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 有哪些看似荒谬,其实很科学的理论@知乎、
- 下一篇: 2019复旦大学计算机分数线,2019复