概述
大家知道 SwiftUI 不仅仅是一款 App 界面布局的超级利器,它同样提供了花样百出的动画和转场机制将 UI 世界点缀的“楚楚动人”。不过,对于苹果开发新入门的秃头小码农来说,使用动画貌似没有想象的那么易如反掌。
如上图所示,在游戏成功和失败时红色圆形到图片的转变并没有产生任何动画效果,这是怎么回事儿?又该如何解决呢?
在本篇博文中,您将学到如下内容:
- “杳无音信”的动画
相信学完本课后,大家会对 SwiftUI 动画及转场的使用重拾自信,并且发出“原来动画是如此多娇”的感叹。
那还等什么呢?Let’s go!!!;)
1. “杳无音信”的动画
我们先来看看大致的 SwiftUI 界面布局逻辑:
ZStack(alignment: .topLeading) {
// 视图背景
Rectangle()
.foregroundStyle(bgColor)
.brightness(startAnim ? 0.3 : 0)
.ignoresSafeArea(edges: .bottom)
.animation(startAnim ? .easeInOut(duration: 1.0).repeatForever() : .none, value: startAnim)
.id(success)
.id(isTimeOver)
ForEach($jailInfos) { $info in
Group {
if isTimeOver {
// 任务失败显示弹孔图片
Image("BulletHole")
.resizable()
.frame(width: info.dia, height: info.dia)
.transition(.scale)
} else if success {
// 任务成功显示寿司图片
Image("Sushi")
.resizable()
.frame(width: info.dia, height: info.dia)
.transition(.scale)
} else {
// 用户按压红色圆形时显示 Jail 闪烁视图
Jail(info: $info)
.transition(.identity)
}
}
.offset(info.offset)
.animation(.bouncy, value: success)
.animation(.bouncy, value: isTimeOver)
}
}
从上面的代码可以看到:
- 我们将背景和每个圆形“监狱”叠加在 ZStack 容器上,并根据不同状态的值来决定如何显示“监狱”的外观;
- 我们用 Group 将所有分支中的“监狱”外观“组合”在一起;
- 我们为前 2 个条件分支中的“监狱”视图增加了相应的转场特效(ScaleTransition);
- 我们用隐式动画为 success 和 isTimerOver 两个状态附着了动画效果;
既然各个分支中的视图都有转场视图修改器,且对应的状态也附加了动画,那为什么它们之间的切换没有转场动画效果呢?
这是因为:这些分支条件中各个视图都放在 Group 中,而在状态发生变化时 Group 视图本身也是“不稳定”的。所以,将隐式动画放在 Group 上并不会起到作用。
一般来说我们有 3 种解决方法:
- 使用显式动画:
withAnimation(.bouncy) {
success = true
}
- 直接在转场对象上应用动画:
Image("Sushi")
.resizable()
.frame(width: info.dia, height: info.dia)
.transition(.scale.animation(.bouncy))
- 在外围稳定的容器上应用隐式动画:
ZStack(alignment: .topLeading) {
//...
}
.animation(.bouncy, value: success)
通过这几种方法,我们可以让丢失的转场动画“失而复得”。
不过,这也随即会带来新的问题:就是动画“格格不入”的位移效果。我们将在下一篇再接再厉,最终给出“天衣无缝”的解决方案,不见不散哦!
总结
在本篇博文中,我们讨论了 SwiftUI 一个“小栗子”中转场动画“不翼而飞”的小问题,并尝试给出初步解决方法。
感谢观赏,下篇再会!8-)