了解了常见的几种手势后,接下来我们了解一下组合手势的操作,当一个视图存在多个手势的时候,为了避免手势冲突,SwiftUI
提供了自定义手势的方法,比如同时进行,顺序进行等等。
以下是一些常见的多种手势组合使用方式:
simultaneously(with:)
:同时使用多个手势,使它们可以同时响应用户的操作。例如,同时使用MagnificationGesture
和RotationGesture
来实现不同的交互效果。sequenced(before:)
:按顺序使用多个手势,确保它们按照特定的顺序依次执行。例如,先执行LongPressGesture
,然后再执行DragGesture
。exclusively(before:)
:在特定条件下使用不同的手势。例如,根据某个条件选择性地使用DragGesture
或TapGesture
。
simultaneously(with:) 同时进行
下面对一个Image
同时添加旋转和放缩的手势,并且在操作的时候两个动画同时进行。
在上面代码中,我们将之前放缩和旋转动画的手势单独提了出来,定义成两个手势属性,这样更便于管理,也会更好阅读代码。关于放缩和旋转代码部分,在之前的文章已经做了详细的说明,这里就不再赘述了。
定义好两个手势属性后,我们给Imag
e添加.gesture
修饰符,并传入下面的组合手势:
magnificationGesture.simultaneously(with: rotationGesture)
或者:
rotationGesture.simultaneously(with: magnificationGesture)
因为是同时进行的两个手势,所以说谁组合谁都一样。
完整代码如下:
struct SimultaneousDemo: View {@GestureState private var scalingRatio: CGFloat = 1.0@State private var lastRatio: CGFloat = 1.0@GestureState private var rotateAngle: Angle = Angle(degrees: 0.0)@State private var lastAngle: Angle = Angle(degrees: 0.0)var magnificationGesture: some Gesture {MagnificationGesture().updating($scalingRatio, body: { value, state, _ instate = value}).onEnded({ value inlastRatio *= value})}var rotationGesture: some Gesture {RotationGesture().updating($rotateAngle, body: { value, state, _ instate = value}).onEnded({ value inlet newDegress = lastAngle.degrees + value.degreeslastAngle = Angle(degrees: newDegress)})}var body: some View {Image("liuyifei").resizable().frame(width: 200, height: 260).scaleEffect(scalingRatio).scaleEffect(lastRatio).rotationEffect(rotateAngle).rotationEffect(lastAngle).gesture(rotationGesture.simultaneously(with: magnificationGesture))}
}
sequenced(before:) 顺序执行
按顺序使用多个手势,确保它们按照特定的顺序依次执行。
例如下面这个示例,先执行LongPressGesture
,然后再执行DragGesture
。
上面代码中同样是将两个手势单独提出来当作属性处理,另外为了测试效果,设置了最短的长按时间,按住时图片放大,1秒后图片变回原形,长按手势结束,此时拖动手势才生效。
再给Image
添加的.gesture
修饰符中传入下面的组合手势,这个可是分顺序的。
longPressGesture.sequenced(before: dragGesture)
完整代码如下:
struct SequencedDemo: View {@GestureState private var isLongPressing = false@State private var dragOffset: CGSize = .zero@State private var position: CGSize = .zerovar longPressGesture: some Gesture {LongPressGesture(minimumDuration: 1).updating($isLongPressing, body: { currentState, state, _ instate = currentState})}var dragGesture: some Gesture {DragGesture().onChanged({ value indragOffset.width = position.width + value.translation.widthdragOffset.height = position.height + value.translation.height}).onEnded({ _ inposition = dragOffset})}var body: some View {Image("liuyifei").resizable().frame(width: 200, height: 260).scaleEffect(isLongPressing ? 1.5 : 1.0).offset(dragOffset).gesture(longPressGesture.sequenced(before: dragGesture))}
}
exclusively(before:)
这种手势组合方式可以根据条件来决定哪个手势应该被触发,从而实现更灵活的交互效果。
下面我演示如何在一个视图上根据条件选择性地使用DragGesture
或LongPressGesture
手势。
为了证明长按是否生效了,这里加了一个弹框,长按生效后弹框。
上面的示例中,在.gesture
修饰符中添加了:
longPressGesture.exclusively(before: dragGesture)
意思就是优先判断LongPressGesture
是否满足,当用户长按视图时,达到长按手势的最短时间后,长按手势生效,此时DragGesture
无效;如果未到长按手势的最短时间就拖拽,那么LongPressGesture
失效。
之前的文章说过LongPressGesture
执行时如果手指移动超过一定距离,那么LongPressGesture
就不满足触发条件了,那么就失效了。
完整代码如下:
struct ExclusivelyDemo: View {@State private var dragOffset: CGSize = .zero@State private var position: CGSize = .zero@State private var isLongPress: Bool = falsevar longPressGesture: some Gesture {LongPressGesture(minimumDuration: 1).onEnded { _ inisLongPress.toggle()}}var dragGesture: some Gesture {DragGesture().onChanged({ value indragOffset.width = position.width + value.translation.widthdragOffset.height = position.height + value.translation.height}).onEnded({ _ inposition = dragOffset})}var body: some View {Image("liuyifei").resizable().frame(width: 200, height: 260).offset(dragOffset).gesture(longPressGesture.exclusively(before: dragGesture)).alert(isPresented: $isLongPress, content: {Alert(title: Text("LongPress手势响应了"))})}
}
写在最后
本篇文章主要介绍了三种手势组合方式,并做了举例,组合方式也比较简单,仅供大家参考。
最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。