了解了常见的几种手势后,接下来我们了解一下组合手势的操作,当一个视图存在多个手势的时候,为了避免手势冲突,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, _ in
state = value
})
.onEnded({ value in
lastRatio *= value
})
}
var rotationGesture: some Gesture {
RotationGesture()
.updating($rotateAngle, body: { value, state, _ in
state = value
})
.onEnded({ value in
let newDegress = lastAngle.degrees + value.degrees
lastAngle = 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 = .zero
var longPressGesture: some Gesture {
LongPressGesture(minimumDuration: 1)
.updating($isLongPressing, body: { currentState, state, _ in
state = currentState
})
}
var dragGesture: some Gesture {
DragGesture()
.onChanged({ value in
dragOffset.width = position.width + value.translation.width
dragOffset.height = position.height + value.translation.height
})
.onEnded({ _ in
position = 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 = false
var longPressGesture: some Gesture {
LongPressGesture(minimumDuration: 1)
.onEnded { _ in
isLongPress.toggle()
}
}
var dragGesture: some Gesture {
DragGesture()
.onChanged({ value in
dragOffset.width = position.width + value.translation.width
dragOffset.height = position.height + value.translation.height
})
.onEnded({ _ in
position = 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手势响应了"))
})
}
}
写在最后
本篇文章主要介绍了三种手势组合方式,并做了举例,组合方式也比较简单,仅供大家参考。
最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。