在 SwiftUI 中,DragGesture 是一种手势,用于检测用户在屏幕上的拖动操作。它可以帮助实现视图的拖拽、滑动和移动等交互行为。
基本语法
DragGesture(minimumDistance: CGFloat = 0, coordinateSpace: CoordinateSpace = .local)
参数:
minimumDistance:手势开始的最小拖动距离(默认值为 0)。用户必须拖动至少这么多,手势才会生效。
coordinateSpace:指定手势的坐标空间(.local 是默认值,表示视图的本地坐标)。
参数解析
.gesture(DragGesture()) 是 SwiftUI 中用于检测拖动手势的一个修饰符。它通过 DragGesture() 来捕获用户的拖动行为,并通过 onChanged 和 onEnded 方法来处理手势的不同阶段。
.gesture(
DragGesture()
.onChanged { value in
// 手势正在进行时,每当用户移动手指,这个闭包都会被调用
}
.onEnded { value in
// 手势结束时调用,例如当用户松开手指
}
)
1、DragGesture()
DragGesture 是一个手势,用于捕获用户在屏幕上的拖动行为。它提供了 onChanged 和 onEnded 两个主要的回调,允许你在手势过程中和手势结束时执行操作。
2、onChanged
onChanged 闭包在用户拖动手指时持续调用。它会不断接收手势的当前状态信息。通过 onChanged,你可以实时更新视图的位置、偏移量等属性。
例如,更新拖动的偏移量:
.onChanged { value in
dragAmount = value.translation // 获取当前的拖动位移
}
3、onEnded
onEnded 闭包在手势结束时调用,例如当用户松开手指时。在这个闭包中,可以做一些收尾的动作,比如将视图恢复到初始位置或执行某些动画。
例如,拖动结束后,重置偏移量:
.onEnded { value in
withAnimation {
dragAmount = .zero // 动画回到初始位置
}
}
在onChanged和onEnded中可以使用 $0:
.onChanged {
dragOffset = $0.translation // 实时更新
}
$0 表示传递给闭包的第一个参数,它是 DragGesture.Value 类型的实例,包含了很多与当前手势相关的信息。
DragGesture.Value 中的所有可用属性
startLocation: 手势的起始位置(CGPoint)。
location: 当前手势的位置(CGPoint)。
translation: 当前手势的偏移量(CGSize)。
predictedEndLocation: 预测的手势结束位置(CGPoint),基于当前的速度。
predictedEndTranslation: 预测的手势结束时的位移(CGSize)。
time: 手势的时间长度(TimeInterval),记录手势持续了多长时间。
DragGesture.Value 的属性
在 onChanged 和 onEnded 闭包中,传递的 value 是 DragGesture.Value 类型的实例,包含以下常用属性:
1、startLocation:手势的起始位置,即手指第一次触碰屏幕的位置。类型为 CGPoint。
let startLocation: CGPoint = value.startLocation
2、location:当前手势的位置,即手指的当前位置。类型为 CGPoint。
let location: CGPoint = value.location
3、translation:从 startLocation 到 location 的位移(拖动的偏移量)。类型为 CGSize,包含 width 和 height 两个值,分别代表水平方向和垂直方向的偏移量。
let translation: CGSize = value.translation
4、time:手势从开始到当前时间的时长。类型为 TimeInterval,记录的是手势持续了多长时间。
let duration: TimeInterval = value.time
使用方法
基本用法
struct ContentView: View {
@State private var offset = CGSize.zero // 偏移量
var body: some View {
Text("Drag me!")
.padding()
.background(Color.blue)
.cornerRadius(10)
.offset(offset) // 根据手势更新位置
.gesture(
DragGesture()
.onChanged { value in
// 实时更新偏移量
offset = value.translation
}
.onEnded { _ in
// 手势结束后复位
offset = .zero
}
)
}
}
效果:
Text 会跟随手指拖动。
当手指抬起时,回到原来的位置。
拖动多视图
struct ContentView: View {
@State private var circleOffset = CGSize.zero
@State private var squareOffset = CGSize.zero
var body: some View {
VStack {
Circle()
.fill(Color.red)
.frame(width: 100, height: 100)
.offset(circleOffset)
.gesture(
DragGesture()
.onChanged { value in
circleOffset = value.translation
}
.onEnded { _ in
circleOffset = .zero
}
)
Rectangle()
.fill(Color.green)
.frame(width: 100, height: 100)
.offset(squareOffset)
.gesture(
DragGesture()
.onChanged { value in
squareOffset = value.translation
}
.onEnded { _ in
squareOffset = .zero
}
)
}
}
}
效果:
圆形和矩形可以独立拖动。
配合动画
struct ContentView: View {
@State private var offset = CGSize.zero
var body: some View {
Circle()
.fill(Color.orange)
.frame(width: 100, height: 100)
.offset(offset)
.gesture(
DragGesture()
.onChanged { value in
offset = value.translation
}
.onEnded { _ in
// 使用动画复位
withAnimation {
offset = .zero
}
}
)
}
}
效果:
当手指抬起时,视图会带有动画返回到原来的位置。
拖动限制范围
struct ContentView: View {
@State private var offset = CGSize.zero
var body: some View {
Circle()
.fill(Color.purple)
.frame(width: 100, height: 100)
.offset(offset)
.gesture(
DragGesture()
.onChanged { value in
// 限制拖动范围
offset = CGSize(
width: min(max(value.translation.width, -100), 100),
height: min(max(value.translation.height, -100), 100)
)
}
.onEnded { _ in
withAnimation {
offset = .zero
}
}
)
}
}
效果:
拖动范围被限制在一个 200×200 的区域。
拖动手势的回调
DragGesture 提供了两个回调方法:
onChanged
在手势变化时触发,实时返回拖动的状态。
参数:
value.translation:相对于手势开始的偏移量。
onEnded
当手势结束时触发。
参数:
value.translation:手势结束时的总拖动偏移量。
示例:onChanged 和 onEnded
struct ContentView: View {
@State private var position = CGSize.zero
@State private var dragOffset = CGSize.zero
var body: some View {
Circle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.offset(x: position.width + dragOffset.width, y: position.height + dragOffset.height)
.gesture(
DragGesture()
.onChanged { value in
dragOffset = value.translation // 实时更新
}
.onEnded { value in
position.width += value.translation.width
position.height += value.translation.height
dragOffset = .zero
}
)
}
}
效果:
圆形会随着手指拖动,并在释放手指后保持最终位置。
拖动手势的典型应用
1、视图拖拽:
在屏幕上移动视图的位置。
2、滑动删除:
实现类似列表项的滑动操作。
3、拖动排序:
用于重新排列项目的顺序。
4、拖拽和放置(Drag and Drop):
将拖拽与放置区域结合,实现更复杂的交互。
5、注意事项
1、与其他手势结合:
可以使用 simultaneously(with:) 或 sequenced(before:) 将拖动手势与其他手势组合。
2、与 offset 一起使用:
通常需要结合 offset 来更新视图的位置。
3、手势范围的坐标系:
默认坐标系是 .local,可以切换为 .global 来检测屏幕级别的拖动。