23、iOS 动画开发:从过渡动画到 3D 效果实现

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 动画的实现方法,为你的应用添加更加丰富和生动的动画效果。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值