Section 05 : Gestures and Events - 手势与事件(10’13")
Make your app interactive and animated with the Drag gesture and events in SwiftUI.
在 SwiftUI 中使用拖动手势和事件为应用制作动画与交互。
手势应用是现在的热门,用户已经习惯使用手势操作应用,而且各种需求也越来越多。在 SwiftUI 中,对手势的开发非常容易。iOS 中的手势有很多,如长按、双击、缩放等。本质上和上一节使用状态控制各种修饰和动画一样,但这次不再是简单的布尔值就可以搞定的。
1. 声明状态 (@State)
在 show 之后再声明一个状态 viewState,这是一个 CGSize 类型,包含了宽度和高度,.zero
是将其宽高都初始化为 0。
@State var viewState = CGSize.zero // 声明一个状态,使用 CGSize 类型,并将其 width 和 height 都初始化为 0
2. 添加 gesture 修饰
(1)在卡片 CardView 上,添加 gesture 修饰。其参数类型为 gesture。这里选择拖拽手势DragGesture()。
(2)DragGesture 提供了如 onChanged,onEnded等事件。这里我们选择 onChanged。并且我们以尾随闭包的形式书写事件响应时的程序。
CardView()
.gesture(
DragGesture().onChanged { value in // 尾随闭包
self.viewState = value.translation // 将拖拽事件的返回值传递给 viewState
}
)
此时在预览窗口并看不出有何变化,这是因为我们还没有使用 viewState 来操作组件。
(3)给卡片添加 offset 修饰。这个修饰要放在手势操作的修饰之前,以免有延迟出现。
CardView()
.offset(x: self.viewState.width, y: self.viewState.height) // 将改变的 viewState 应用在组件的偏移量
.gesture(
DragGesture().onChanged { value in // 尾随闭包
self.viewState = value.translation // 将拖拽事件的返回值传递给 viewState
}
)
此时可以拖动卡片了,但是松开鼠标左键时,卡片并没有回到初始位置。而且有时候拖拽的时候,卡片的起始移动位置是随机的。所以要进一步完善代码。
(4)在 onChanged 的闭包之后,增加 onEnded 事件响应。类似 onChanged 的事件,我们也写个尾随闭包。
CardView()
.offset(x: self.viewState.width, y: self.viewState.height) // 将改变的 viewState 应用在组件的偏移量
.blendMode(.hardLight)
.onTapGesture { // 定义响应的动作
self.show.toggle() // 切换 show 的状态
}
.gesture(
DragGesture().onChanged { value in // 拖拽事件的尾随闭包
self.viewState = value.translation // 将拖拽事件的返回值传递给 viewState
}
.onEnded { value in // 拖拽结束,松开鼠标左键,另一个尾随闭包
self.viewState = .zero // 复位卡片位置
}
)
现在好了,卡片可以自动复位了。**注意:**这里并未使用动画。
(5)在卡片 CardView 上添加动画修饰,这也需要在手势操作之前。(还记得修饰器的应用是有顺序的吗?🤪)这次我们选择 spring
动画。
CardView()
.offset(x: self.viewState.width, y: self.viewState.height) // 将改变的 viewState 应用在组件的偏移量
.blendMode(.hardLight)
.animation(.spring(response: 0.3, dampingFraction: 0.5, blendDuration: 0)) // 使复位的动作更连贯
.onTapGesture { // 定义响应的动作
self.show.toggle() // 切换 show 的状态
}
.gesture(
DragGesture().onChanged { value in // 拖拽事件的尾随闭包
self.viewState = value.translation // 将拖拽事件的返回值传递给 viewState
}
.onEnded { value in // 拖拽结束,松开鼠标左键,另一个尾随闭包
self.viewState = .zero // 复位卡片位置
}
)
Spring 动画
是 iOS 中常见的动画形式,是一种动画计时,因为有动量,所以可以反弹。由于模拟了物理学的状态,所以距离越远,动量越大,复位的速度就越快。
默认值不是很明显,另一个 spring 的构造函数提供了更多的调整参数。
response:数值越小,复位越快,建议值为 0.3-0.6。
dampingFraction:数值越小,反弹越大,弹簧效果越明显。建议值 0.6 。
blendDuration:视频说猜测为动画过渡的时间
3. 修改其他卡片
为了让另外两张卡片能跟随我们拖动的卡片,可以给它们也添加上 offiset 修饰器。复制下面的代码,粘贴到每个 BackCardView中现有的 offset 修饰的后面。
.offset(x: self.viewState.width, y: self.viewState.height)
预览中可以看见另外两张卡片也跟随一起移动了。
4. 完善动画
(1)在 onChanged 的响应中,增加对状态 show 的更新。键入下面的代码到 onChanged 的代码块中。
self.show = true
通过预览,可以看见拖动时,其他两张卡片为展开的状态。
(2)在 onEnded 的响应中,也加上对状态 show 的更新。键入下面的代码到 onEnded 的代码块中。
self.show = false
这样可以在释放鼠标左键时,所有的卡片都复位到最初的位置。
总结
- 状态可以用来控制控件的行为,状态要在 body 之前声明,根据要控制的行为选择状态的变量类型。
- 手势的开发应用中最常遇到的,用户通过手势进行操作并与应用交互。常见的手势有 DragGesture、TapGesture、LongPressGesture、RotationGesture 等。
- 手势通过响应事件修改状态。一般可能响应的事件有:onChanged、onEnded 。
- 将修改的状态应用到组件的修饰器上,组件即可以按照手势的操作改变展示。
- Spring 动画是一种具有动量的动画,主要参数有 response、dampingFraction 等。
接下来
深入研究动画中的定时曲线、延时速度和重复。