SwiftUI 中的基础动画和过渡(二)

在上一篇文章中,我们已经介绍了 SwiftUI 中的隐式动画显式动画

在 SwiftUI中,只需要更多的关注动画的开始和结束,动画的过程由 SwiftUI 帮我们自动完成。接着我们来实现一些常见的动画效果。

创建一个加载动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct ContentView: View {
@State var isLoading: Bool = false
var body: some View {
ZStack() {
Circle()
.trim(from: 0, to: 0.8)
.stroke(lineWidth: 10)
.fill(.green)
.rotationEffect(.degrees(isLoading ? 360 : 0))
.animation(.default.repeatForever(autoreverses: false), value: isLoading)
}
.frame(width: 100, height: 100)
.onAppear {
isLoading = true
}
}
}

在上面的代码中,

  1. 使用 Circle创建了一个非闭合的圆环;
  2. Cricle添加了rotationEffect修饰器实现旋转的效果,它需要一个角度作为参数;
  3. Circle添加了隐式动画和动态变量isLoading进行绑定;
  4. 当整个视图显式的时候,将动态变量isLoading的值改为true,开始动画;

这里的animation动画的效果我们使用的是默认default,然后链式调用了 repeatForever方法,这个方法中可以设置动画是否反转。

不反转:

反转:

即设置为反转后,动画结束后会回到动画开始前的效果。

如果我们想要改变动画的速度,只需要将默认default动画设置为linear,然后修改duration参数即可。如下:

1
.animation(.linear(duration: 1).repeatForever(autoreverses: false), value: isLoading)

duration值越大,动画速度越慢,即持续时间越长。

另外,onAppear修饰器是 SwiftUI 中视图的生命周期函数,类似于 UIKit的中viewDidAppear方法,它的作用是当视图出现在屏幕是自动调用。

类似地,我们可以对代码稍加修改即可实习下面的这样效果:

修改后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct ContentView: View {
@State var isLoading: Bool = false
var body: some View {
ZStack() {
Circle()
.stroke(lineWidth: 10)
.fill(Color(.systemGray5))
Circle()
.trim(from: 0, to: 0.2)
.stroke(lineWidth: 10)
.fill(.green)
.rotationEffect(.degrees(isLoading ? 360 : 0))
.animation(.linear(duration: 1).repeatForever(autoreverses: false), value: isLoading)
}
.frame(width: 100, height: 100)
.onAppear {
isLoading = true
}
}
}

动画延迟

通过设置duration可以修改动画的速度,同样地可以通过设置delay来设置的延迟效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct ContentView: View {
@State var isLoading: Bool = false
var body: some View {
HStack() {
ForEach(0..<4, id: \.self) { index in
Circle()
.fill(.green)
.frame(width: 10, height: 10)
.scaleEffect(isLoading ? 0.3 : 1)
.animation(.linear(duration: 0.6).repeatForever(autoreverses: true).delay(0.2 * Double(index)), value: isLoading)
}
}
.onAppear {
isLoading = true
}
}
}

在上面的代码中,我们使用ForEach创建了四个相同的Circle,然后都给它们添加了一个大小改变的修饰器sacleEffect,动画效果我们设置为linear,链式调用了repeatForeverdeplay方法。

delay通过0.2*Double(index)设置,即四个Circle的延迟动画时间分别为:0,0.2,0.4,0.6。通过设置延迟时间,可以让Circle动画的开始时间不同。 如果没有这个延迟时间,所有的Circle都将同时开始出现动画效果。

效果如下:

矩形变换为圆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
struct ContentView: View {
@State var recordBegin: Bool = false
@State var recording: Bool = false
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: recordBegin ? 30 : 5)
.fill(recordBegin ? .red : .green)
.frame(width: recordBegin ? 60 : 240, height: 60)
.overlay {
Image(systemName: "mic.fill")
.font(.title)
.foregroundStyle(.white)
.scaleEffect(recording ? 0.7 : 1.0)
}
RoundedRectangle(cornerRadius: recordBegin ? 35 : 10)
.trim(from: 0, to: recordBegin ? 0 : 1)
.stroke(lineWidth: 5)
.fill(.green)
.frame(width: recordBegin ? 70 : 250, height: 70)
}
.onTapGesture {
withAnimation(.linear(duration: 0.3)) {
recordBegin.toggle()
}

withAnimation(.default.repeatForever().delay(0.5)) {
recording.toggle()
}

}
}
}

在上面的代码中,我们通过两个状态变量控制RoundedRectangleframecornerRadius来实现一个圆角矩形到圆的变换效果。

效果如下: