在 SwiftUI 的 DragGesture
中,DragGesture.Value
是非常重要的类型,封装了用户手势的实时数据(例如当前手指位置、起点位置、相对偏移量、滑动速度等)。其中,属性 predictedEndLocation
是用于预测拖拽手势的 结束位置,这是基于手势的当前速度和方向推算出来的。
以下是对 DragGesture.Value
和 predictedEndLocation
的体系化介绍,包括它们的用途、属性、实用场景以及完整应用案例。
1. DragGesture.Value
简介
#
DragGesture.Value
是 SwiftUI 手势系统提供的一个结构体,专用于传递 DragGesture
(拖动手势) 的实时状态信息。用户拖拽过程中,每一帧都会触发手势的更新,这些数据由 DragGesture.Value
承载。
重要角色 #
- 实时捕捉用户手指的位置信息:如开始位置(
startLocation
)、当前位置(location
)。 - 提供拖拽位移信息:捕获拖动的距离(
translation
)。 - 为后续动画预测行为:通过
predictedEndLocation
判断手势完成后的惯性结果,决定动画方向/速度。
主要属性 #
以下是 DragGesture.Value
的核心属性(SwiftUI 官方定义)及其用途:
属性名 | 类型 | 用途 |
---|---|---|
startLocation | CGPoint | 手势的起始位置(即用户开始拖拽时手指的初始位置)。 |
location | CGPoint | 手势的当前触摸位置(即用户手指当前拖拽到的位置)。 |
translation | CGSize | 拖拽的累计偏移量,即从起始位置到当前位置的 x 和 y 方向上的偏移(location - startLocation )。 |
predictedEndLocation | CGPoint | iOS 系统预测的手势拖拽自然结束位置,基于当前速度和方向预测的坐标点,主要用于惯性动画效果。 |
velocity | CGVector | 拖拽手势的实时速度(x 和 y 两个方向上的分量)。 |
属性解释:predictedEndLocation
#
1. 定义 #
predictedEndLocation
是一个便捷的系统预测值,它表示 iOS 系统预测手势在当前趋势下自然停止时的位置,预测基于手势的速度、方向和当前点。
优点 | 用途场景 |
---|---|
在手势完成(抬手)前预测手势终点,模拟惯性滑动时更加自然。 | - 实现惯性滑动的动画终点位置。 - 判断向哪个方向惯性滚动。 |
提供一种近似模拟的方式来估算系统结束的滑动位置,在手势快速拖动中非常实用。 | - 实现卡片或列表的滑动动画。 - 判断是否完成翻页或触发新动作。 |
2. 使用 DragGesture.Value
的场景
#
2.1 拖拽交互场景 #
在拖动对象(如视图、卡片)的过程中,你可以实时读取用户的 当前位置 和 偏移位移,并利用这些属性更新视图的动态位置。此外,还能通过 predictedEndLocation
预测惯性效果。
2.2 惯性滑动动画 #
结合 predictedEndLocation
,你可以为手势结束(即手指离开屏幕)后的视图设置滑动效果。例如:
- 拖动的视图,根据惯性滑到 “下一页” 或 “上一页”。
- 拖动之后自定义弹回或移除(如卡片动画)。
2.3 速度控制的滑动效果 #
利用 velocity
或 predictedEndLocation
,可以根据手势的速率调整动画的时间或滑动的距离。例如,用户滑动越快时,动画的时间越短,滑动距离越远。
3. 使用示例:逐项演示 #
下面用示例分步讲解如何使用 DragGesture.Value
的每个组成部分,特别是 predictedEndLocation
的实际用途。
3.1 基础拖拽效果 #
通过 location
和 translation
计算拖拽中视图的实时位置。
示例代码: #
import SwiftUI
struct DragGestureView: View {
@State private var offset: CGSize = .zero // 视图的实时偏移
var body: some View {
Circle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.offset(offset) // 动态偏移视图位置
.gesture(DragGesture()
.onChanged { value in
self.offset = value.translation // 实时偏移值
}
.onEnded { _ in
self.offset = .zero // 手指抬起后重置位置
}
)
}
}
3.2 利用 predictedEndLocation
实现惯性滑动
#
当用户手指抬起后,根据预测的终点位置触发 “惯性滑动动画”。
示例代码: #
import SwiftUI
struct PredictedEndLocationExample: View {
@State private var offset: CGSize = .zero // 当前偏移量
var body: some View {
Circle()
.fill(Color.green)
.frame(width: 100, height: 100)
.offset(offset) // 动态偏移圆的位置
.gesture(
DragGesture()
.onChanged { value in
self.offset = value.translation // 实时更新偏移量
}
.onEnded { value in
// 获取预测的终点位置
let predictedEnd = value.predictedEndLocation
let current = value.location
// 计算预测惯性位移
let inertialOffset = CGSize(
width: predictedEnd.x - current.x,
height: predictedEnd.y - current.y
)
// 动画移动到预测终点
withAnimation(.easeOut) {
self.offset.width += inertialOffset.width
self.offset.height += inertialOffset.height
}
}
)
}
}
关键点: #
- 实时更新偏移量: 在手势更新时,根据实时
translation
更新圆的位置。 - 预测终点: 当手势完成时,使用预测的
predictedEndLocation
。 - 惯性动画: 动画模拟视图滑向系统预测的终点位置,提供流畅的用户体验。
3.3 滑动方向判断:基于 predictedEndLocation
#
通过判断 predictedEndLocation
和 location
之间的差值判断滑动方向(左右滑动)。
示例代码: #
import SwiftUI
struct SwipeDirectionExample: View {
var body: some View {
VStack {
Text("Swipe to See Direction")
.font(.headline)
.padding()
Color.gray
.frame(height: 200)
.gesture(
DragGesture()
.onEnded { value in
let velocity = value.predictedEndLocation.x - value.location.x
if velocity > 0 {
print("Swiped Right")
} else {
print("Swiped Left")
}
}
)
}
}
}
关键逻辑:
- 如果
predictedEndLocation.x > location.x
:用户右滑(手指向右拖拽)。 - 如果
predictedEndLocation.x < location.x
:用户左滑(手指向左拖拽)。
3.4 卡片滑动效果 #
用拖拽结合惯性判断,让卡片移除或回归初始位置。
示例代码: #
import SwiftUI
struct CardSwipeExample: View {
@State private var offset: CGSize = .zero // 偏移量
var body: some View {
ZStack {
ForEach(0..<3, id: \.self) { index in
ZStack {
Color.blue
Text("Card \(index + 1)")
.font(.title)
.foregroundColor(.white)
}
.frame(width: 300, height: 400)
.cornerRadius(20)
.offset(offset)
.gesture(
DragGesture()
.onChanged { value in
self.offset = value.translation
}
.onEnded { value in
let velocity = value.predictedEndLocation.x - value.location.x
withAnimation(.spring()) {
// 移除或回归初始位置
if abs(velocity) > 200 {
self.offset.width += velocity > 0 ? 500 : -500 // 向右或向左滑动移除
} else {
self.offset = .zero // 恢复初始位置
}
}
}
)
}
}
}
}
关键点:
velocity
判断滑动速度:快滑时,移除卡片;慢滑时回弹到原位置。- 惯性滑动:通过
predictedEndLocation
决定滑动的终点与方向。
4. 总结:DragGesture.Value
与 predictedEndLocation
的作用
#
属性 | 用途 |
---|---|
startLocation | 记录手势起点,常用于计算位移或拖拽比例。 |
location | 获取用户当前拖拽的坐标位置,实时更新视图位置。 |
translation | 获取相对偏移(当前位置 - 起点位置),常用于动态更新视图的拖拽平移效果。 |
predictedEndLocation | 预测系统计算的终点位置,常用于惯性动画,例如快速滑动卡片的去向或视图弹回的终点。 |
速度计算公式 | velocity = predictedEndLocation.x - location.x 用于计算滑动速度,判断方向及动画持续时间等。 |
通过熟练掌握 DragGesture.Value
,尤其是 predictedEndLocation
的应用,你可以构建出非常流畅自然的手势交互和惯性动画。如果有更复杂的需求或进一步的问题,欢迎随时提问! 😊
predictedEndLocation #
这行代码:
let velocity = value.predictedEndLocation.x - value.location.x
主要是用于 计算用户手势的水平滑动速度(拖拽速度),在动画拖拽中,这是一个非常关键的计算,用于确定用户当前滑动手势的动态趋势(比如滑动快慢或者拖拽意图),从而指导动画的表现(例如惯性滑动的方向和距离)。
1. 参数解释 #
在拖拽手势中,value
是 DragGesture.Value
,它描述了当前手势的状态以及各种属性。
value.location
:- 用户手指当前触碰的屏幕坐标(
CGPoint
)。 - 在这里我们取的是
x
坐标(水平位置)。
- 用户手指当前触碰的屏幕坐标(
value.predictedEndLocation
:- 系统预测 用户手指在当前滑动速度和方向下,可能结束触摸的位置(也是以一个屏幕坐标
CGPoint
表示)。 - 这个预测是基于手势的当前速度估算的,提供的值可以帮助我们模拟滑动的惯性。
- 系统预测 用户手指在当前滑动速度和方向下,可能结束触摸的位置(也是以一个屏幕坐标
predictedEndLocation.x - location.x
:- 表示 手指当前位置与预测终点之间的水平距离。
- 这个值反映了:以当前速度继续滑动的情况下,手指在 x 方向上可能滑动的距离。
2. 这行代码的作用 #
这行代码是用来 近似计算手势的水平滑动速度。
公式:
velocity = predictedEndLocation.x - location.x
velocity 的含义:
- 它表示当前拖拽手势的水平瞬时速度(以每次手势帧捕获的单位为参考)。
- 值越大,说明用户的水平滑动越快,越小则说明滑动较慢。
用途:
- 用在后续惯性动画中,决定动画的方向、加速度和持续时间。
- 根据用户拖拽手势速度决定视图的 “是否完成某种动作”(例如页面翻页、内容进入或返回)。
3. 具体场景应用 #
以下是动画拖拽场景中,这行代码的具体用途:
3.1 惯性滑动 #
基于用户的滑动速度,模拟滑动的惯性动画。
- 如果 velocity 很大(用户快速滑动),惯性滑动的距离就会较远。
- 如果 velocity 很小(用户慢速拖动),惯性滑动的距离就会较短。
例如,在一个 卡片滑动 动画中:
示例: #
.onEnded { value in
let velocity = value.predictedEndLocation.x - value.location.x
withAnimation {
if abs(velocity) > 200 { // 假设 200 是滑动速度的临界值
// 执行惯性滑动动画(如移除卡片)
self.cardPosition = .offscreen
} else {
// 回到初始位置(用户没有滑动足够快)
self.cardPosition = .onscreen
}
}
}
- 用户快速滑动卡片触发 “彻底移除”。
- 用户慢速操作,卡片会回弹到原位置。
3.2 翻页控制 #
当你做类似 分页动画(Paging) 时,使用 velocity
可以判断用户是 “上一页” 还是 “下一页”,并确定页切换的动画方向。
示例: #
.onEnded { value in
let velocity = value.predictedEndLocation.x - value.location.x
if velocity > 0 {
// 用户快速右滑,翻到上一页
self.currentPage -= 1
} else {
// 用户快速左滑,翻到下一页
self.currentPage += 1
}
}
- 正值(
velocity > 0
):用户往右拖,翻到上一页。 - 负值(
velocity < 0
):用户往左拖,翻到下一页。
3.3 判断方向与拖拽目的 #
您可以通过 velocity 的正负值 来判断用户的滑动方向:
velocity > 0
:表示用户手势水平向右(拖拽目标往右)。velocity < 0
:表示用户手势水平向左(拖拽目标往左)。
在 UI 动画中,这种判断常见于:
- 滑动关闭页面(右滑返回)。
- 从左向右拖动时触发视图解锁。
3.4 惯性动画的 “速度” 与 “距离” 结合使用 #
为了更精确地模拟现实中的惯性滑动,velocity
还可以和手指移动的 距离 或 时间 相结合计算加速度。
公式:
let speed = velocity / timeDelta // 速度
let distance = velocity * 0.5 // 惯性滑动距离(简单估算公式)
示例: #
.onEnded { value in
let velocity = value.predictedEndLocation.x - value.location.x
withAnimation(.easeOut(duration: abs(velocity) / 500)) { // 动画时间基于速度
if velocity > 0 {
// 模拟右滑动的惯性动画
self.cardOffset = CGSize(width: 500, height: 0)
} else {
// 模拟左滑动的惯性动画
self.cardOffset = CGSize(width: -500, height: 0)
}
}
}
解释:
abs(velocity) / 500
动态控制动画时间(速度越大,动画越短,体现惯性越强)。cardOffset
决定手势拖拽后动画的最终位置。
4. 延伸知识:velocity 的含义为什么与速度相关? #
在 SwiftUI 的 DragGesture.Value
中,predictedEndLocation
是基于当前手势的拖拽速度估算的(这由 iOS 系统通过内部算法计算)。简单来说:
predictedEndLocation
是依据当前滑动速度推算出的位置。- 如果用户滑动得很快,则预测的终点会更远;如果滑动得慢,则预测终点更接近当前位置。
- 两者的差值
predictedEndLocation.x - location.x
正好可以反映速度(速度越大,差值越明显)。
5. 总结 #
代码 | 作用 |
---|---|
let velocity = value.predictedEndLocation.x - value.location.x | 用于计算用户水平拖拽的瞬时速度(基于当前位置和预测位置的差值)。 |
主要用途: | |
1. 惯性动画:如滑动卡片的惯性去向(参考滑动速度)。 | |
2. 翻页:确定用户是否滑动到下一页或上一页(通过速度方向判断)。 | |
3. 滑动方向的判断:右滑(正值)或左滑(负值)。 | |
4. 动态固化动画时间:速度大时动画时间短,速度小时动画时间长,体现惯性滑动的自然效果。 |
配合惯性滑动、分页动画和手势逻辑,velocity
是动画控制中一个核心的计算指标。如果需要更深入的拖拽动画案例实现,欢迎随时交流! 😊