隐式动画
在 SwiftUI 中,最简单的动画类型是隐式动画 ,利用 animation()修饰符
每次点击按钮时更改缩放效果值 ;通过添加修饰符scaleEffect
使按钮使用其缩放 ;添加修饰符animation
来缩放 动画。iOS 默认选择弹簧动画,因为它们模仿我们在现实世界中习惯的动画。另外,我们可以通过向修饰符传递不同的值来控制所使用的动画类型。例如,使用.linear
使动画从开始到结束以恒定速度移动 。
该隐式动画会对更改的视图的所有属性生效,这意味着如果我们将更多动画修改器附加到视图,那么它们都会一起更改。
当我们将animation()修改器附加到视图时,只要我们正在观察的值发生变化,SwiftUI 就会使用默认的系统动画自动为该视图发生的任何更改设置动画。 这些都是高度可定制的:
自定义隐式动画
您可以大致控制弹簧需要多长时间才能完成,以及弹簧应该有多大的弹性 - 是否应该或多或少地向后和向前反弹,其中 1 是最大弹性,0 是没有弹性。
.animation(.spring(duration: 2,bounce: 0.9), value: animationAmount)
我们可以自定义动画,并将持续时间指定为秒数。如,获得持续两秒的缓入动画:
.animation(.easeInOut(duration: 2), value: animationAmount)
还可加入延迟
.animation(.easeInOut(duration: 2).delay(1), value: animationAmount)
还可以要求动画重复一定次数,甚至可以通过设置autoreverses为 true 来使其前后反弹。
.animation(.easeInOut(duration: 2).repeatCount(3, autoreverses: true), value: animationAmount)
永远重复
.animation(.easeInOut(duration: 2).repeatForever(autoreverses: true), value: animationAmount)
自定义效果,覆盖圆圈不断放大和淡出:
动画绑定
该animation()修饰符可以应用于任何 SwiftUI 绑定,这会导致该值在当前值和新值之间进行动画处理。
按钮平滑地放大或缩小 - 它不只是直接跳到比例 2、3 和 4。这里实际发生的是 SwiftUI 正在检查绑定更改之前视图的状态,检查绑定更改后视图的目标状态,然后应用动画从 A 点到达 B 点。
这就是为什么我们可以为布尔值变化设置动画:Swift 并不是以某种方式在 false 和 true 之间发明新值,而只是为由于变化而发生的视图变化设置动画。
这些绑定动画有效地扭转了隐式动画的局面 :我们现在不在视图上设置任何内容,而是通过状态更改显式地为其设置动画
,而不是在视图上设置动画并通过状态更改隐式为其设置动画。在前者中,状态变化不知道它会触发动画,而在后者中,视图不知道它会被动画化------两者都有效,而且都很重要。
显式动画
第三种创建动画的有用方法:显式询问 用于以动画形式呈现因状态更改而发生的更改。 即,在发生某些任意状态更改时发生动画:它没有附加到绑定,也没有附加到视图,只是我们明确要求发生特定的动画,因为状态改变。
这仍然并不意味着我们手动创建动画的每一帧 - 这仍然是 SwiftUI 的工作,并且它继续通过查看应用状态更改之前和之后视图的状态来计算动画。
示例
当点击该按钮时,我们将使其以 3D 效果旋转
新的修饰符 ,rotation3DEffect()它可以指定一个以度为单位的旋转量以及一个确定视图如何旋转的轴。
如果我们只是编写animationAmount += 360
而没有withAnimation {}
,那么更改将 立即 发生,没有动画效果,因为按钮上没有附加动画修改器。
withAnimation()可以给定一个动画参数,使用您可以在 SwiftUI 中其他地方使用的所有相同动画。例如,我们可以使用withAnimation()如下调用使我们的旋转效果使用弹簧动画:
javascript
withAnimation(.spring(duration: 1, bounce: 0.5)) {
animationAmount += 360
}
高级动画
控制动画堆栈
- 修饰符顺序很重要,因为 SwiftUI 按照修饰符的应用顺序包装视图。
若顺序不对,则无动画效果
- 我们可以将animation()修改器应用于视图,以便让它隐式地以动画方式进行更改。
示例:修改按钮代码,使其根据某些状态显示不同的颜色
您将看到点击按钮会使其颜色在蓝色和红色之间变化。
如果我们应用多个animation()修改器,每个修改器都会控制下一个动画之前的所有内容。这使我们能够以各种不同的方式对状态变化进行动画处理,而不是对所有属性统一进行处理。
如,我们可以使用默认动画使颜色发生变化,但使用弹簧作为剪辑形状:
也可以通过传递 nil 给修改器来禁用动画。例如,您可能希望颜色立即发生变化,但圆角形状保留其动画,您可以这样写:
动画手势
示例
我们可以在屏幕上拖动一个视图,但当我们放开它时,它会弹回到原来的位置。
您将看到现在可以拖动渐变卡,当您释放时,它将跳回到中心。该卡的偏移量由dragAmount 确定,而该偏移量又由拖动手势设置。
当然,你也可以通过显示动画方式 实现
ini
.onEnded { _ in
withAnimation(.bouncy) {
dragAmount = .zero
}
}
如果我们将偏移动画与拖动手势和一点 延迟 结合起来,我们就可以创建非常有趣的动画,而无需大量代码。
您将看到可以拖动任何字母以使整个字符串跟随,短暂的延迟会导致蛇状效果。当您释放拖动时,SwiftUI 还会添加颜色变化,即使字母移回中心也会在蓝色和红色之间产生动画。
显示/隐藏视图
SwiftUI 最强大的功能之一是能够自定义视图显示和隐藏的方式。
将看到,红色矩形淡入淡出,同时还将按钮向上移动以腾出空间。
可以通过添加修改器transition()
来使矩形添加 放大和缩小 效果:
scss
Rectangle()
.fill(.pink)
.frame(width: 200, height: 200)
.transition(.scale)
一个有用的方法是.asymmetric
,它允许我们在视图显示时使用一种过渡,在视图消失时使用另一种过渡
less
Rectangle()
.fill(.pink)
.frame(width: 200, height: 200)
.transition(.asymmetric(insertion: .scale, removal: .opacity))
使用 ViewModifier 构建自定义过渡
自定义过渡动画,效果:使我们的视图从一个角向内旋转,而不会脱离其应有的边界
swift
struct CornerRotateModifier: ViewModifier {
// 控制旋转角度
let amount: Double
// 旋转的锚点------视图的哪一部分应该固定在适当的位置作为旋转的中心
let anchor: UnitPoint
func body(content: Content) -> some View {
content.rotationEffect(.degrees(amount), anchor: anchor)
.clipped() // 当视图旋转时,位于其自然矩形之外的部分不会被绘制
}
}
less
extension AnyTransition{
static var pivot: AnyTransition {
.modifier(
active: CornerRotateModifier(amount: 90, anchor: .topLeading),
identity: CornerRotateModifier(amount: 0, anchor: .topLeading)
)
}
}
使用以下方法将枢轴动画附加到任何视图 :.transition(.pivot)