在 SwiftUI 中,只需要定义【开始】和【结束】的两个状态,剩下两个状态之间的【变化阶段】,系统将自动为你处理。一般数值型的参数属性,都可以呈现动画。您可以通过不同的方式来控制变化,例如速度和持续时间。SwiftUI 动画大体设置原理是:
@State 状态值的改变@State 状态值的改变引发了视图的某个参数值的改变
<aside> 💡
几乎所有大部分的属性都可以应用动画,只要它们符合 numerical 协议。如果不符合,swiftUI 就无法自动插值动画。
</aside>
当我们使用 .default、 .easeInOut 这些动画曲线时,实际上是在创建一个 Animation 结构体的实例,它们有自己的属性和修饰符。
您可以从各种内置选项中指定所需曲线,包括以下这些。它们语法都一样,都是 .animation(.xxxxxx, value: )
.default 默认效果.linear 恒定的线性速度.easeIn 开始缓慢然后加速直至结束.easeOut 开始时很快,然后在接近结束时减慢.easeInOut 开始时很慢,中间加速,然后在接近尾声时减速.spring 弹簧动画,iOS 17 之后它可以设置 bounce 参数.smooth 是没有弹跳的弹簧动画(来自 iOS 17).snappy 是一个带有一点反弹的弹簧动画(来自 iOS 17).bouncy 是具有中等弹跳量的弹簧动画(来自 iOS 17).timingCurve 来指定自己的曲线控制点详细解释具体参见: 自定义动画 Timing
如果没有设置该属性,则SwiftUI会用默认的时间,大概是0.4秒左右完成动画
// 完成时间 2 秒
.animation(.easeOut(duration: 2))
// 完成时间 15 秒
.animation(.linear(duration: 15))
所有 Animation 值,无论是缓动、弹簧还是自定义动画,都支持一组修改器,这些修改器可以在不改变其底层时间曲线的情况下调整动画的行为。这些修改器可以直接链接到任何动画值,并且可以以任意顺序组合使用。
delay(_:) 修饰符会将动画的开始时间推迟指定的秒数,这在交错多个元素时非常有用,因为每个元素都会在前一个元素稍晚一些开始。
VStack(spacing: 16) {
Rectangle()
.frame(width: 250, height: 80)
.offset(x: showRects ? 0 : -340)
.animation(.smooth, value: showRects)
Rectangle()
.frame(width: 250, height: 80)
.offset(x: showRects ? 0 : -340)
.animation(.smooth.delay(0.15), value: showRects)
Rectangle()
.frame(width: 250, height: 80)
.offset(x: showRects ? 0 : -340)
.animation(.smooth.delay(0.3), value: showRects)
}
使用 delay 延迟时,有时动画曲线的前面必须得加上 Animation 关键词,否则会提示“类型不明确”
//依次延迟的效果
HStack {
ForEach(0...4, id: \.self) {
index in
Circle()
.frame(width: 10, height: 10)
.foregroundColor(.green)
.scaleEffect(self.isLoading ? 0 : 1)
.animation(Animation.linear(duration: 0.6).repeatForever().delay(0.2 * Double(index)))
}
}
.onAppear() {
self.isLoading = true
}

speed(_:) 修饰符会按一定比例缩放动画的持续时间,其中 2.0 会使动画速度加倍, 0.5 会使动画速度减半。它可用于在不同速度下重复使用同一条动画曲线,而无需重新定义。
Rectangle()
.frame(width: 250, height: 250)
.offset(x: isVisible ? 0 : -340)
.animation(.bouncy.speed(2), value: isVisible)
设置速度 和 duration 一样,也可以控制一个动画完成的快慢。
.animation(Animation.easeInOut(duration: duration))
.animation(Animation.easeInOut(duration: duration).speed(2))
.animation(Animation.easeInOut(duration: duration).speed(0.5))

repeatCount(_:autoreverses:) 修饰符用于指定动画的运行次数。当 autoreverses 为 true ,动画每次重复都会改变方向。当 autoreverses 为 false` 时,动画值会在每次循环之间跳回起始位置。
Rectangle()
.frame(width: 250, height: 250)
.scaleEffect(isAnimating ? 1.3 : 1)
.animation(
.easeInOut(duration: 0.4).repeatCount(3, autoreverses: true),
value: isAnimating
)
默认情况下,当使用 repeatCount 时,SwiftUI会将动画自动反转进行。 并且点击按钮后,动画播放3次后停止;这个时候再点击按钮,这个动画实际是以反转的效果开始,再运行3次。所以前后两次点击的效果其实不大一样。类似(1,2,1)和(2,1,2)

[repeatForever(autoreverses:)](<https://developer.apple.com/documentation/swiftui/animation/repeatforever%28autoreverses%3A%29>) 修饰符的作用相同,但会无限循环播放。将 autoreverses 设置为 false 通常用于连续循环的动画,例如旋转指示器,以确保动画运动流畅无阻。
Rectangle()
.frame(width: 250, height: 250)
.rotationEffect(.degrees(isAnimating ? 360 : 0))
.animation(
.linear(duration: 4).repeatForever(autoreverses: false),
value: isAnimating
)
// 当触发一个动画时,默认只会执行一遍。如果想重复需要额外设置:
// 重复,默认反转
.animation(Animation.easeOut(duration: 0.6).repeatCount(3))
// 重复,但不反转
.animation(Animation.easeOut(duration: 0.6).repeatCount(3, autoreverses: false))
// 一直重复
.animation(Animation.easeOut(duration: 0.6).repeatForever())
// 一直重复,但不反转
.animation(Animation.easeOut(duration: 0.6).repeatForever(autoreverses: false))
// 每次重复之前设置一个延迟间隔
.animation(Animation.easeOut(duration: 0.6).delay(1).repeatForever(autoreverses: false))
// 如果将重复计数设置为2双数,那么按钮将先放大然后再缩小,然后立即跳回到较大状态。这是因为最终按钮必须与程序的状态匹配,无论我们应用什么动画
.animation(.easeInOut(duration: 1).repeatCount(3, autoreverses: true), value: animationAmount)
隐式动画:指的是通过视图修饰符 .animation(_: , value:) 添加动画,SwiftUI 自动处理状态变化的动画效果。
animation( _: value: ) 比之前的写法就是多了一个参数 value,它要在这里填上能够触发这个动画的具体 state 参数// 首先:因为希望在某种条件下,更改某个修饰器的值,且执行某个动画;因此首先需要使用 @State 属性来存储某个状态;
@State private var heartActived : Bool = false
// 然后:在具体视图的属性上,使用三元运算符,定义不同状态下,UI是什么样的
ZStack {
Circle()
.foregroundColor(heartActived ? .gray : .red)
.scaleEffect(heartActived ? 1.0 : 0.5)
}
// 最后:把 animation(_: value:) 修饰器加到想要呈现动画的视图上,並指定所需要到动画类型
// 此时视图就会在参数发生改变时呈现动画,还可以定义动画持续时间与延迟时间
// 不止可以在单个视图中应用 animation 修饰器,也可以在父视图中使用。SwiftUI 会寻找嵌入在父视图中所有的改变状态,并建立动画。
// 这个 animation 修饰器是加到 ZStack 的,所以它会查找 ZStack 里,和 heartActived 相关的所有状态参数,赋予动画
ZStack{...}
.animation(.bouncy(duration: 0.5), value: heartActived)
// 最后的最后,通过在具体时机改变该状态参数的值,即可随时触发动画
.OnTapGesture{
self.heartActived.toggle()
}
// 在不想要动画的视图下加上以下修饰符
.animation(.none)
.animation(nil)
@State private var showCard = false
@State private var useAnimation = false
VStack {
HStack {
Button(action: {
self.useAnimation = false
self.showCard.toggle()
}) {
Text("X").font(.body).padding(8)
}
}
Image("Card")
}
.offset(x: showCard ? 0 : -400)
// 使用三元运算符判断 useAnimation 的值,决定有没有动画
.animation(useAnimation ? .default : .none)
animation() 修饰符应用于视图,以便让它隐式地以动画方式进行更改