在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
修饰,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
的数值,图片只有在手势完成之后才显示出来缩放。
以上的三段代码展示了三个回调函数的作用。那么我们要完成一个图片的缩放功能是如何实现的呢。代码如下
@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
类型的属性。通过这个属性的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 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个回调函数 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 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)?)
,可以对比方式一的函数写代码。
updating(_:body:)
,onChanged(_:)
,onEnd(_:)
。手势 *** 作的变量修饰:@GestureState
View的定位函数offset
多个手势可以通过gesture
多次捕捉。当然还有simultaneousGesture
,highPriorityGesture
处理不同情况。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)