iOS 动画开发:UIPercentDrivenInteractiveTransition 与 UIViewPropertyAnimator 实战
1. UIPercentDrivenInteractiveTransition 实现交互过渡
在自定义过渡动画中,通过采用 UIPercentDrivenInteractiveTransition 协议,可以轻松为自定义过渡添加交互性。交互过渡通常由用户手势驱动, UIPanGestureRecognizer 是一个能提供连续手势反馈的实用类。还可以通过设置 UIPercentDrivenInteractiveTransition 的 interactive 属性值,在交互和非交互过渡模式之间进行切换。
下面是一个使弹出过渡具有交互性的挑战示例:
- 步骤 1:在 DetailViewController 中创建弱引用属性以持有动画器
- 从导航控制器栈中获取 MainViewController 的动画器对象。
- 步骤 2:为 DetailViewController 添加平移手势处理程序
- 该处理程序应与 MainViewController 中的方法几乎相同,但区别在于它应弹出当前视图控制器,而不是调用 segue。
以下是关键代码示例:
// 在 DetailViewController 中
weak var animator: YourAnimatorClass?
override func viewDidLoad() {
super.viewDidLoad()
if let mainVC = navigationController?.viewControllers.first as? MainViewController {
animator = mainVC.animator
}
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
view.addGestureRecognizer(panGesture)
}
@objc func handlePanGesture(_ gesture: UIPanGestureRecognizer) {
// 处理手势逻辑,弹出当前视图控制器
if gesture.state == .ended {
navigationController?.popViewController(animated: true)
}
}
2. UIViewPropertyAnimator 基础介绍
UIViewPropertyAnimator 是一个帮助开发者创建交互式、可中断视图动画的类。在 iOS 10 之前,创建基于视图的动画只能使用 UIView.animate(withDuration:...) 系列 API,这些 API 无法让开发者暂停或停止正在运行的动画。而 UIViewPropertyAnimator 可以让开发者控制正在运行的动画,调整它们,并提供有关动画当前状态的详细信息。
不过, UIView.animate(withDuration:...) API 在创建 iOS 动画中仍然发挥着重要作用,因为它们简单易用,适用于一些不需要中断或反转的简单动画。同时, UIViewPropertyAnimator 并没有实现 UIView.animate(withDuration:...) 的所有功能,所以有时仍需要使用旧的 API。
3. 基本动画实现
以下是使用 UIViewPropertyAnimator 创建基本动画的步骤:
1. 打开并运行项目 :启动项目后,会看到一个类似于 iOS 锁屏的界面,初始视图控制器显示一个搜索栏、一个小部件和底部的编辑按钮。
2. 创建初始动画 :打开 LockScreenViewController.swift ,添加 viewWillAppear(_:) 方法,对表格视图进行缩放和透明处理。
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.transform = CGAffineTransform(scaleX: 0.67, y: 0.67)
tableView.alpha = 0
}
- 创建动画器 :在
viewDidAppear(_:)方法中,使用UIViewPropertyAnimator的便利初始化器创建动画器。
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let scale = UIViewPropertyAnimator(duration: 0.33, curve: .easeIn)
}
UIViewAnimationCurve 枚举提供了以下曲线选项:
| 选项 | 描述 |
| ---- | ---- |
| easeInOut | 先慢后快再慢 |
| easeIn | 先慢后快 |
| easeOut | 先快后慢 |
| linear | 匀速 |
- 添加动画 :使用
addAnimations方法添加动画块,可以添加多个动画块,还可以设置延迟。
scale.addAnimations {
self.tableView.alpha = 1.0
}
scale.addAnimations({
self.tableView.transform = .identity
}, delayFactor: 0.33)
- 添加完成块 :使用
addCompletion方法添加完成块,当动画完成时执行相应操作。
scale.addCompletion { _ in
print("ready")
}
- 启动动画 :调用
startAnimation()方法启动动画。
scale.startAnimation()
4. 动画代码抽象
为了使代码更简洁,将动画代码提取到一个单独的文件中。创建 AnimatorFactory.swift 文件,并添加以下代码:
import UIKit
enum AnimatorFactory {
static func scaleUp(view: UIView) -> UIViewPropertyAnimator {
let scale = UIViewPropertyAnimator(duration: 0.33, curve: .easeIn)
scale.addAnimations {
view.alpha = 1.0
}
scale.addAnimations({
view.transform = CGAffineTransform.identity
}, delayFactor: 0.33)
scale.addCompletion { _ in
print("ready")
}
return scale
}
}
然后在 LockScreenViewController.swift 中替换 viewDidAppear(_:) 方法:
override func viewDidAppear(_ animated: Bool) {
AnimatorFactory.scaleUp(view: tableView).startAnimation()
}
5. 运行动画器
如果只需要一个动画块并且希望立即运行,可以使用 UIViewPropertyAnimator.runningPropertyAnimator 类方法。以下是一个在用户使用搜索栏时淡入模糊层,搜索完成时淡出模糊层的示例:
func toggleBlur(_ blurred: Bool) {
UIViewPropertyAnimator.runningPropertyAnimator(
withDuration: 0.5, delay: 0.1, options: .curveEaseOut,
animations: {
self.blurView.alpha = blurred ? 1 : 0
},
completion: nil
)
}
同时,实现 UISearchBarDelegate 方法来触发动画:
extension LockScreenViewController: UISearchBarDelegate {
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
toggleBlur(true)
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
toggleBlur(false)
}
func searchBarResultsListButtonClicked(_ searchBar: UISearchBar) {
searchBar.resignFirstResponder()
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.isEmpty {
searchBar.resignFirstResponder()
}
}
}
6. 基本关键帧动画
可以在 UIViewPropertyAnimator 动画块中使用 UIView.animate 和 UIView.animateKeyframes 。以下是创建一个简单抖动关键帧动画的步骤:
1. 创建抖动动画方法 :在 AnimatorFactory.swift 中添加 jiggle(view:) 方法。
@discardableResult
static func jiggle(view: UIView) -> UIViewPropertyAnimator {
return UIViewPropertyAnimator.runningPropertyAnimator(
withDuration: 0.33, delay: 0, animations: {
UIView.animateKeyframes(withDuration: 1, delay: 0,
animations: {
UIView.addKeyframe(withRelativeStartTime: 0.0,
relativeDuration: 0.25) {
view.transform = CGAffineTransform(rotationAngle: -.pi / 8)
}
UIView.addKeyframe(withRelativeStartTime: 0.25,
relativeDuration: 0.75) {
view.transform = CGAffineTransform(rotationAngle: +.pi / 8)
}
UIView.addKeyframe(withRelativeStartTime: 0.75,
relativeDuration: 1.0) {
view.transform = CGAffineTransform.identity
}
},
completion: nil)
},
completion: { _ in
view.transform = .identity
}
)
}
- 在单元格中调用动画 :打开
IconCell.swift,添加iconJiggle()方法。
func iconJiggle() {
AnimatorFactory.jiggle(view: icon)
}
- 触发动画 :在
WidgetView.swift中,当用户点击集合视图单元格时,调用iconJiggle()方法。
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? IconCell {
cell.iconJiggle()
}
}
通过以上步骤,我们可以利用 UIPercentDrivenInteractiveTransition 和 UIViewPropertyAnimator 实现丰富多样的动画效果,提升用户体验。
下面是一个简单的 mermaid 流程图,展示基本动画的创建流程:
graph LR
A[打开项目] --> B[创建初始动画]
B --> C[创建动画器]
C --> D[添加动画]
D --> E[添加完成块]
E --> F[启动动画]
综上所述, UIPercentDrivenInteractiveTransition 和 UIViewPropertyAnimator 为 iOS 动画开发提供了强大的功能和灵活性,开发者可以根据具体需求选择合适的方法来实现各种动画效果。同时,通过代码抽象和合理的设计,可以使代码更加简洁易维护。在实际开发中,还可以进一步探索这些 API 的更多用法,创造出更加精彩的动画效果。
iOS 动画开发:UIPercentDrivenInteractiveTransition 与 UIViewPropertyAnimator 实战
7. 交互过渡与手势驱动的详细分析
在使用 UIPercentDrivenInteractiveTransition 实现交互过渡时,手势驱动是核心。以 UIPanGestureRecognizer 为例,它能提供连续的手势反馈,让过渡动画随着用户的手势操作而动态变化。
下面详细分析手势处理的逻辑:
// 在 DetailViewController 中
weak var animator: YourAnimatorClass?
var interactiveTransition: UIPercentDrivenInteractiveTransition?
override func viewDidLoad() {
super.viewDidLoad()
if let mainVC = navigationController?.viewControllers.first as? MainViewController {
animator = mainVC.animator
}
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
view.addGestureRecognizer(panGesture)
}
@objc func handlePanGesture(_ gesture: UIPanGestureRecognizer) {
let translation = gesture.translation(in: view)
let progress = abs(translation.x) / view.bounds.width
switch gesture.state {
case .began:
interactiveTransition = UIPercentDrivenInteractiveTransition()
navigationController?.popViewController(animated: true)
case .changed:
interactiveTransition?.update(progress)
case .ended, .cancelled:
if progress > 0.5 {
interactiveTransition?.finish()
} else {
interactiveTransition?.cancel()
}
interactiveTransition = nil
default:
break
}
}
上述代码中,手势处理逻辑分为三个阶段:
- 开始阶段( .began ) :创建 UIPercentDrivenInteractiveTransition 实例,并触发弹出过渡动画。
- 变化阶段( .changed ) :根据手势的平移距离计算过渡进度,并使用 update(_:) 方法更新过渡动画。
- 结束或取消阶段( .ended 或 .cancelled ) :根据进度判断是否完成过渡,如果进度大于 0.5,则完成过渡;否则取消过渡。
以下是手势处理逻辑的 mermaid 流程图:
graph LR
A[手势开始] --> B[创建过渡实例并触发动画]
B --> C{手势变化?}
C -- 是 --> D[计算进度并更新动画]
C -- 否 --> E{手势结束或取消?}
E -- 是 --> F{进度 > 0.5?}
F -- 是 --> G[完成过渡]
F -- 否 --> H[取消过渡]
E -- 否 --> C
8. UIViewPropertyAnimator 的状态管理
UIViewPropertyAnimator 提供了丰富的状态管理功能,开发者可以暂停、继续、反转和停止动画。以下是一些常用的状态管理方法:
| 方法 | 描述 |
| ---- | ---- |
| pauseAnimation() | 暂停正在运行的动画 |
| continueAnimation(withTimingParameters:durationFactor:) | 继续暂停的动画 |
| reverse() | 反转动画的播放方向 |
| stopAnimation(_:) | 停止动画,可以选择是否完成当前帧 |
以下是一个简单的示例,展示如何使用这些方法:
var animator: UIViewPropertyAnimator?
func setupAnimator() {
animator = UIViewPropertyAnimator(duration: 1.0, curve: .easeInOut) {
// 动画代码
self.view.alpha = 0
}
animator?.addCompletion { _ in
print("Animation completed")
}
}
func pauseAnimation() {
animator?.pauseAnimation()
}
func continueAnimation() {
animator?.continueAnimation(withTimingParameters: nil, durationFactor: 1.0)
}
func reverseAnimation() {
animator?.reverse()
}
func stopAnimation() {
animator?.stopAnimation(true)
}
9. 动画性能优化
在使用 UIViewPropertyAnimator 进行动画开发时,性能优化是一个重要的考虑因素。以下是一些优化建议:
- 减少不必要的动画 :避免同时运行过多的动画,只在必要时触发动画。
- 使用轻量级视图 :尽量使用轻量级的视图进行动画,减少内存开销。
- 优化动画参数 :合理设置动画的持续时间、延迟和曲线,避免过于复杂的动画效果。
- 异步处理 :对于一些耗时的动画操作,可以考虑使用异步线程进行处理,避免阻塞主线程。
以下是一个优化动画性能的示例:
func performOptimizedAnimation() {
DispatchQueue.global().async {
// 模拟耗时操作
Thread.sleep(forTimeInterval: 0.1)
DispatchQueue.main.async {
let animator = UIViewPropertyAnimator(duration: 0.3, curve: .easeInOut) {
self.view.alpha = 0
}
animator.startAnimation()
}
}
}
10. 总结与展望
通过前面的介绍,我们深入了解了 UIPercentDrivenInteractiveTransition 和 UIViewPropertyAnimator 在 iOS 动画开发中的应用。 UIPercentDrivenInteractiveTransition 让我们能够实现交互过渡动画,增强用户体验;而 UIViewPropertyAnimator 则提供了强大的动画创建和管理功能,包括基本动画、关键帧动画、状态管理等。
在实际开发中,我们可以根据具体需求选择合适的动画方法,并结合性能优化技巧,创建出流畅、美观的动画效果。未来,随着 iOS 系统的不断更新,这些动画 API 可能会提供更多的功能和优化,开发者可以持续关注并探索更多的可能性。
以下是一个总结性的 mermaid 流程图,展示整个动画开发的流程:
graph LR
A[选择动画类型] --> B{交互过渡?}
B -- 是 --> C[使用 UIPercentDrivenInteractiveTransition]
B -- 否 --> D[使用 UIViewPropertyAnimator]
C --> E[处理手势驱动]
D --> F[创建动画器]
F --> G[添加动画和完成块]
G --> H[启动动画]
E --> I[根据手势更新过渡]
H --> J{需要状态管理?}
J -- 是 --> K[使用状态管理方法]
J -- 否 --> L[结束动画]
I --> M{手势结束?}
M -- 是 --> N{完成过渡?}
N -- 是 --> L
N -- 否 --> O[取消过渡]
M -- 否 --> I
总之,掌握 UIPercentDrivenInteractiveTransition 和 UIViewPropertyAnimator 是 iOS 动画开发的重要技能,开发者可以利用这些工具创造出更加出色的应用程序。
超级会员免费看

被折叠的 条评论
为什么被折叠?



