iOS 动画开发:从过渡动画到 3D 效果实现
1. 可中断过渡动画
在开发过程中,我们常常需要实现一些过渡动画,并且希望这些动画可以被用户中断。下面是实现可中断过渡动画的具体步骤:
-
设置非交互式过渡
:在代码中,为编辑按钮分配一个闭包,在
self.presentSettings()
之前插入
self.presentTransition.wantsInteractiveStart = false
,以确保以非交互式方式呈现设置视图控制器。示例代码如下:
(cell as? FooterCell)?.didPressEdit = { [unowned self] in
self.presentTransition.wantsInteractiveStart = false
self.presentSettings()
}
-
允许交互
:切换到
PresentTranstion.swift文件,在transitionAnimator(using:)方法底部插入animator.isUserInteractionEnabled = true,使过渡动画可交互,用户暂停后能继续与屏幕交互。
// 在 transitionAnimator(using:) 底部添加
animator.isUserInteractionEnabled = true
-
跟踪触摸事件
:在
LockScreenViewController.swift中添加一个新属性touchesStartPointY,用于存储触摸起始点的 Y 坐标。当用户在过渡期间触摸屏幕时,暂停过渡并存储触摸位置。
var touchesStartPointY: CGFloat?
override func touchesBegan(
_ touches: Set<UITouch>,
with event: UIEvent?
) {
guard presentTransition.wantsInteractiveStart == false,
presentTransition.animator != nil else {
return
}
touchesStartPointY = touches.first?.location(in: view).y
presentTransition.pause()
}
- 处理触摸移动 :根据用户触摸移动的方向和距离,完成或取消过渡。
override func touchesMoved(
_ touches: Set<UITouch>,
with event: UIEvent?
) {
guard
let startY = touchesStartPointY,
let currentPoint = touches.first?.location(in: view).y
else {
return
}
if currentPoint < startY - 40 {
touchesStartPointY = nil
presentTransition.animator?.addCompletion { _ in
self.blurView.effect = nil
}
presentTransition.cancel()
} else if currentPoint > startY + 40 {
touchesStartPointY = nil
presentTransition.finish()
}
}
2. 简单 3D 动画
在一些应用中,我们可能需要为界面添加 3D 效果,下面以一个折叠式侧边菜单为例,介绍如何实现简单的 3D 动画。
-
创建 3D 变换
:
- 打开项目,观察初始效果。
- 打开
ContainerViewController.swift
文件,添加一个类方法
menuTransform(percent:)
来创建对应于侧边菜单“打开程度”百分比的 3D 变换。
func menuTransform(percent: CGFloat) -> CATransform3D {
var identity = CATransform3DIdentity
identity.m34 = -1.0 / 1000
let remainingPercent = 1.0 - percent
let angle = remainingPercent * .pi * -0.5
let rotationTransform = CATransform3DRotate(
identity, angle, 0.0, 1.0, 0.0)
let translationTransform = CATransform3DMakeTranslation(
menuWidth * percent, 0, 0)
return CATransform3DConcat(
rotationTransform, translationTransform)
}
- 在 `setMenu(toPercent:)` 方法中使用 `menuTransform(percent:)` 更新菜单变换。
// 移除原有修改菜单原点的代码
// menuViewController.view.frame.origin.x =
// menuWidth * CGFloat(percent) - menuWidth
// 添加新代码
menuViewController.view.layer.transform =
menuTransform(percent: percent)
- 移动图层锚点 :默认情况下,图层的锚点 x 坐标为 0.5,将其设置为 1.0 可以使菜单围绕其右边缘旋转。
// 在设置菜单框架之前插入
menuViewController.view.layer.anchorPoint.x = 1.0
menuViewController.view.frame = CGRect(
x: -menuWidth, y: 0,
width: menuWidth, height: view.frame.height)
- 通过阴影创建透视效果 :通过改变菜单的透明度来模拟阴影效果,增强 3D 动画的真实感。
// 在 setMenu(toPercent:) 中添加
menuViewController.view.alpha = CGFloat(max(0.2, percent))
- 提高效率的光栅化 :为了避免菜单项目边框出现像素化问题,在动画开始时进行光栅化,结束后关闭。
// 在 handleGesture() 的 .began 块末尾添加
menuViewController.view.layer.shouldRasterize = true
menuViewController.view.layer.rasterizationScale =
UIScreen.main.scale
// 在 .ended, .cancelled, .failed 块的动画完成闭包中添加
self.menuViewController.view.layer.shouldRasterize = false
3. 相机距离参考
不同的相机距离会产生不同的透视效果,以下是一些常见相机距离的参考:
| 相机距离范围 | 效果描述 |
| ---- | ---- |
| 0.1…500 | 非常近,有大量透视畸变 |
| 750…2,000 | 透视效果好,内容清晰可见 |
| 2,000 及以上 | 几乎没有透视畸变 |
4. 3D 动画挑战
创建菜单按钮的 3D 旋转动画,使其在用户滑动时与菜单视图控制器一起旋转。
// 在 setMenu(toPercent:) 中添加
let centerVC = centerViewController.viewControllers.first as?
CenterViewController
// 调整按钮 imageView 的 3D 变换
5. 中级 3D 动画
以一个飓风图像画廊为例,介绍如何创建多个视图的 3D 动画。
-
显示图像
:在
ViewController.swift
的
viewDidAppear(_:)
方法中添加所有图像到视图控制器的视图,并设置锚点和框架。
for image in images {
image.layer.anchorPoint.y = 0.0
image.frame = view.bounds
view.addSubview(image)
}
- 设置导航标题 :为了更明显地显示当前显示的飓风图像,设置导航标题。
navigationItem.title = images.last?.title
-
设置透视效果
:在
viewDidAppear(_:)中为视图控制器的视图设置透视效果。
var perspective = CATransform3DIdentity
perspective.m34 = -1.0 / 250.0
view.layer.sublayerTransform = perspective
6. 总结
通过上述步骤,我们可以实现可中断过渡动画和 3D 动画效果。在实现过程中,需要注意相机距离、图层锚点、光栅化等因素对动画效果和性能的影响。掌握这些技术可以为 iOS 应用添加更加生动和吸引人的动画效果。
7. 流程图
graph TD;
A[开始] --> B[设置非交互式过渡];
B --> C[允许交互];
C --> D[跟踪触摸事件];
D --> E[处理触摸移动];
E --> F[创建 3D 变换];
F --> G[移动图层锚点];
G --> H[通过阴影创建透视效果];
H --> I[提高效率的光栅化];
I --> J[3D 动画挑战];
J --> K[中级 3D 动画];
K --> L[结束];
iOS 动画开发:从过渡动画到 3D 效果实现
8. 关键要点回顾
在实现这些动画效果时,有几个关键要点需要牢记:
-
3D 变换的 m34 值
:
CATransform3D
的
.m34
值对于为 3D 变换提供透视效果非常重要。通过设置该值,可以调整相机距离,从而影响场景的透视效果。
-
图层锚点
:图层的锚点决定了变换的中心点。合理设置锚点可以让动画按照预期的方式进行,例如让菜单围绕特定的边缘旋转。
9. 操作步骤总结
为了更清晰地展示实现这些动画的步骤,我们将其总结如下:
1.
可中断过渡动画
- 为编辑按钮闭包添加非交互式过渡设置。
- 允许过渡动画交互。
- 跟踪触摸事件并暂停过渡。
- 根据触摸移动完成或取消过渡。
2.
简单 3D 动画
- 创建 3D 变换方法。
- 使用变换方法更新菜单变换。
- 移动图层锚点。
- 通过改变透明度模拟阴影效果。
- 进行光栅化提高效率。
3.
中级 3D 动画
- 显示图像并设置锚点和框架。
- 设置导航标题。
- 为视图设置透视效果。
10. 代码示例总结
以下是各个部分的代码示例总结:
| 功能 | 代码示例 |
| ---- | ---- |
| 可中断过渡动画 |
swift<br>(cell as? FooterCell)?.didPressEdit = { [unowned self] in <br> self.presentTransition.wantsInteractiveStart = false<br> self.presentSettings() <br>}<br>// PresentTranstion.swift 中<br>animator.isUserInteractionEnabled = true<br>// LockScreenViewController.swift 中<br>var touchesStartPointY: CGFloat?<br>override func touchesBegan( <br> _ touches: Set<UITouch>, <br> with event: UIEvent? <br>) { <br> guard presentTransition.wantsInteractiveStart == false, <br> presentTransition.animator != nil else { <br> return <br> } <br> touchesStartPointY = touches.first?.location(in: view).y <br> presentTransition.pause() <br>}<br>override func touchesMoved( <br> _ touches: Set<UITouch>, <br> with event: UIEvent? <br>) { <br> guard <br> let startY = touchesStartPointY, <br> let currentPoint = touches.first?.location(in: view).y <br> else { <br> return <br> } <br> if currentPoint < startY - 40 { <br> touchesStartPointY = nil <br> presentTransition.animator?.addCompletion { _ in <br> self.blurView.effect = nil <br> } <br> presentTransition.cancel() <br> } else if currentPoint > startY + 40 { <br> touchesStartPointY = nil <br> presentTransition.finish() <br> } <br>}
|
| 简单 3D 动画 |
swift<br>func menuTransform(percent: CGFloat) -> CATransform3D { <br> var identity = CATransform3DIdentity <br> identity.m34 = -1.0 / 1000<br> let remainingPercent = 1.0 - percent <br> let angle = remainingPercent * .pi * -0.5<br> let rotationTransform = CATransform3DRotate( <br> identity, angle, 0.0, 1.0, 0.0) <br> let translationTransform = CATransform3DMakeTranslation( <br> menuWidth * percent, 0, 0) <br> return CATransform3DConcat( <br> rotationTransform, translationTransform)<br>}<br>// setMenu(toPercent:)<br>menuViewController.view.layer.transform = <br> menuTransform(percent: percent)<br>// 设置锚点<br>menuViewController.view.layer.anchorPoint.x = 1.0<br>menuViewController.view.frame = CGRect( <br> x: -menuWidth, y: 0, <br> width: menuWidth, height: view.frame.height)<br>// 阴影效果<br>menuViewController.view.alpha = CGFloat(max(0.2, percent))<br>// 光栅化<br>// handleGesture() 的 .began 块末尾<br>menuViewController.view.layer.shouldRasterize = true <br>menuViewController.view.layer.rasterizationScale = <br> UIScreen.main.scale<br>// .ended, .cancelled, .failed 块的动画完成闭包中<br>self.menuViewController.view.layer.shouldRasterize = false
|
| 中级 3D 动画 |
swift<br>// viewDidAppear(_:)<br>for image in images { <br> image.layer.anchorPoint.y = 0.0 <br> image.frame = view.bounds <br> view.addSubview(image) <br>}<br>navigationItem.title = images.last?.title<br>var perspective = CATransform3DIdentity <br>perspective.m34 = -1.0 / 250.0 <br>view.layer.sublayerTransform = perspective
|
11. 效果展示与分析
通过实际运行代码,我们可以观察到不同动画效果的呈现。
-
可中断过渡动画
:用户可以在过渡过程中暂停、继续或取消动画,增加了交互的灵活性。
-
简单 3D 动画
:菜单从平面变为具有深度的 3D 效果,通过旋转和移动,呈现出更加立体的视觉效果。阴影效果的添加进一步增强了真实感。
-
中级 3D 动画
:飓风图像画廊中的图像可以以 3D 方式展开,用户可以更直观地查看所有图像。
12. 注意事项
在实现这些动画时,还需要注意以下几点:
-
代码顺序
:某些代码的执行顺序非常重要,例如设置图层锚点需要在设置视图框架之前,否则可能会导致视图位置偏移。
-
性能优化
:光栅化虽然可以提高动画的显示效果,但会增加内存使用。因此,需要在动画开始时开启,结束时关闭,以避免不必要的内存开销。
13. 流程图解释
graph TD;
A[开始] --> B[设置非交互式过渡];
B --> C[允许交互];
C --> D[跟踪触摸事件];
D --> E[处理触摸移动];
E --> F[创建 3D 变换];
F --> G[移动图层锚点];
G --> H[通过阴影创建透视效果];
H --> I[提高效率的光栅化];
I --> J[3D 动画挑战];
J --> K[中级 3D 动画];
K --> L[结束];
这个流程图展示了从可中断过渡动画到中级 3D 动画的实现流程。首先进行过渡动画的设置,包括非交互式过渡和交互设置。然后跟踪触摸事件并处理,接着进入 3D 动画的实现,包括创建变换、移动锚点、添加阴影效果和光栅化等步骤。最后完成 3D 动画挑战和中级 3D 动画的实现。
通过以上的介绍和总结,希望你能够掌握 iOS 动画开发中可中断过渡动画和 3D 动画的实现方法,为你的应用添加更加丰富和生动的动画效果。
超级会员免费看
1016

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



