SwiftUI 手势 *** 作

SwiftUI 手势 *** 作,第1张

基本概念

在ios中我们经常用到的手势 *** 作有点击,长按,拖拽,缩放,旋转。在手势 *** 作时,不同的手势有不同的触发函数,比如,点击在点击完成时触发某个函数,拖拽手势不仅仅在拖拽完成时触发函数,而在拖拽的过程中也可以触发两个函数,在这两个函数中捕捉到不同的参数。对于手势的捕获也有两种方式,我们具体的看下这两种方式

方式一,通过gesture捕获手势

我们可以给View添加一个gesture()的装饰函数来捕捉手势,在这个函数中设定不同Gesture()就可以捕捉到不同的手势。在捕捉到手势之后,还有3个回调函数处理不同的情况,updating(_:body)在手势 *** 作时被调用,这个函数需要绑定一个@GestureState修饰的变量,这个变量在回调时被修改,但只是临时的修改,手势 *** 作完这个变量会被修改为手势 *** 作之前的初始化值.onChange(_:)这个同样是在手势 *** 作时被调用,这个函数中修改的变量在手势 *** 作结束时被保留。onEnd()这个函数时在手势 *** 作成功之后调用。

缩放

我们设定一个图片缩放场景来演示缩放手势,先通过image显示一张图片,我们通过缩放手势让图片放大或者缩小。捕获的gesture是MagnificationGesture.代码如下

@GestureState var zoom:CGFloat=1

Image("j1").resizable().frame(width: 200, height: 200, alignment: .leading).scaleEffect(zoom).gesture(MagnificationGesture().updating($zoom, body: {
                gestureZoom,z,transaction in
                z=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修饰,onChangez是缩放比例,我们将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的数值,图片只有在手势完成之后才显示出来缩放。
以上的三段代码展示了三个回调函数的作用。那么我们要完成一个图片的缩放功能是如何实现的呢。代码如下

@State var zoom:CGFloat=1

Image("j1").resizable().frame(width: 200, height: 200, alignment: .leading).scaleEffect(zoom).gesture(MagnificationGesture().onChanged({z in
                zoom += z>1 ? 0.05 : -0.05 }))
通过onChange控制图片缩放,手势 *** 作结束之后图片依旧保持缩放状态。没有直接将z 赋值给zoom,而是判断z是放大还是缩小,>1时zoom增加,图片放大,<1时zoom减小,图片缩小。这样做的原因是为了保持图片的连续放大或者缩小。如果我们直接赋值给zoom
,假设上次手势是放大了3倍,而这次手势也是放大 *** 作,但只放大了2倍,那么图片就会由放大3倍变成放大2倍,实际变小了,我们期望是图片继续放大的,这样的结果不是我们期望的。所以,我们根据手势的 *** 作来设置放大比例,手势只要是放大 *** 作,那么放大比例就增加,保证图片是放大的。 拖拽

我们设定场景是,通过拖拽让图片在不同的位置显示。捕获的Gesture是DragGesture.代码如下

@GestureState var dragOffset=CGSize.zero

Image("j1").resizable().frame(width: 200, height: 200, alignment: .leading).offset(x: dragOffset.width, y: dragOffset.height).gesture(DragGesture().updating($dragOffset, body: { gesture,dragoff,transaction in
                dragoff.width=gesture.translation.width
                dragoff.height=gesture.translation.height
            }))

我们先在updating处理,这个需要绑定的是一个CGSize类型的属性。通过这个属性的widthheight来设置图片的偏移位置,在拖拽结束时,图片会到原先的位置。

@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 in
                offsetX=v.translation.width
                offsetY=v.translation.height
            }))

这段代码是在手势 *** 作结束捕获。图片会移动到手指抬起的位置。手指移动时没有任何变化。

@State var offsetX:CGFloat=0
@State var offsetY:CGFloat=0
let step:CGFloat=3

Image("j1").resizable().frame(width: 200, height: 200, alignment: .leading).offset(x: offsetX, y: offsetY).gesture(DragGesture().onChanged({gesture in
                offsetX+=gesture.translation.width>0 ? step : -step
                offsetY+=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 in
                    ganlge=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个回调函数 updatingonEnd,在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 in
                print("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=false

Image("j1").resizable().frame(width: 200, height: 200, alignment: .leading).rotationEffect(angle).gesture(LongPressGesture().updating($longPress, body: {b ,state,transaction in
                print("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)?),可以对比方式一的函数写代码。

总结 手势捕捉的两种方式。每个手势捕捉之后有3个回调函数updating(_:body:),onChanged(_:),onEnd(_:)。手势 *** 作的变量修饰:@GestureStateView的定位函数offset多个手势可以通过gesture多次捕捉。当然还有simultaneousGesturehighPriorityGesture处理不同情况。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/989512.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存