SwiftUI提供两种类型的动画:隐式和显式。两种方法都可以为视图设置动画和视图过渡。为了实现隐式动画,框架提供了一个称为的修饰符animation。可以将此修改器附加到要设置动画的视图,并指定首选的动画类型。(可选)可以定义动画的持续时间和延迟。然后,SwiftUI将根据视图的状态变化自动渲染动画。显式动画对您要呈现的动画提供了更有限的控制。无需向视图附加修饰符,而是告诉SwiftUI您要在**withAnimation()**块内设置动画的状态变化。
隐式动画
看一下上图。这是一个简单的可点击视图,由红色圆圈和心脏组成。当用户点击心脏或圆圈时,圆圈的颜色将更改为浅灰色,而心脏的颜色将更改为红色。同时,心脏图标的大小会变大。因此,我们在这里进行各种状态更改:
- 圆圈的颜色从红色变为浅灰色。
- 心脏图标的颜色从白色变为红色。
- 心形图标将其原始大小加倍。
如果使用SwiftUI实现可轻按的圆圈,则代码如下所示:
//
// ContentView.swift
// ContentView
//
// Created by Apple on 2021/2/5.
//
import SwiftUI
struct ContentView: View {
@State private var circleColorChanged = false
@State private var heartColorChanged = false
@State private var heartSizeChanged = false
var body: some View {
ZStack {
Circle()
.frame(width: 200, height: 200)
.foregroundColor(circleColorChanged ? Color(.systemGray5) : .red)
Image(systemName: "heart.fill")
.foregroundColor(heartColorChanged ? .red : .white)
.font(.system(size: 100))
.scaleEffect(heartSizeChanged ? 1.0 : 0.5)
}
// .animation(.default)
.animation(.spring(response: 0.3, dampingFraction: 0.3, blendDuration: 0.3))
.onTapGesture {
self.circleColorChanged.toggle()
self.heartColorChanged.toggle()
self.heartSizeChanged.toggle()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
我们定义了三个状态变量,以将初始值设置为false来对状态进行建模。要创建圆圈和心脏,我们使用ZStack将心脏图像叠加在圆圈顶部。SwiftUI带有onTapGesture用于检测轻击手势的修饰符。可以将其附加到任何视图以使其可点击。在onTapGesture闭包中,我们切换状态以更改视图的外观。
.animation(.spring(response: 0.3, dampingFraction: 0.3, blendDuration: 0.3))
这将渲染基于弹簧的动画,使心脏产生颠簸的效果。您可以调整阻尼和混合值以获得不同的效果。
显式动画
让我们看看如何使用显式动画获得相同的结果。如前所述,您需要将状态更改包装在withAnimation块中。要创建相同的动画效果,可以编写如下代码:
ZStack {
Circle()
.frame(width: 200, height: 200)
.foregroundColor(circleColorChanged ? Color(.systemGray5) : .red)
Image(systemName: "heart.fill")
.foregroundColor(heartColorChanged ? .red : .white)
.font(.system(size: 100))
.scaleEffect(heartSizeChanged ? 1.0 : 0.5)
}
.onTapGesture {
withAnimation(.spring(response: 0.3, dampingFraction: 0.3, blendDuration: 0.3)) {
self.circleColorChanged.toggle()
self.heartColorChanged.toggle()
self.heartSizeChanged.toggle()
}
}
我们不再使用的animation而不是我们总结的代码中onTapGesture有withAnimation该withAnimation调用采用动画参数。
使用显式动画,可以轻松控制要动画的状态。例如,如果您不希望对心脏图标的大小进行动画处理,则可以从以下代码中排除该行代码withAnimation:
.onTapGesture {
withAnimation(.spring(response: 0.3, dampingFraction: 0.3, blendDuration: 0.3)) {
self.circleColorChanged.toggle()
self.heartColorChanged.toggle()
}
self.heartSizeChanged.toggle()
}
在这种情况下,SwiftUI将仅对圆圈和心脏的颜色变化进行动画处理。您将不再看到心脏图标的动画效果。
使用RotationEffect创建加载指示器
SwiftUI动画的强大功能在于您无需担心如何对视图进行动画处理。您需要提供的是开始和结束状态。然后,SwiftUI会找出其余的内容。如果您了解此概念,则可以创建各种类型的动画。
例如,让我们创建一个简单的加载指示器,创建如上图所示的加载指示器,我们可以从如下所示的开放式圆圈开始:
import SwiftUI
struct ContentView: View {
@State private var isLoading = false
var body: some View {
ZStack {
Circle()
.stroke(Color(.systemGray5), lineWidth: 14)
.frame(width: 100, height: 100)
Circle()
.trim(from: 0, to: 0.2)
.stroke(Color.green, lineWidth: 7)
.frame(width: 100, height: 100)
.rotationEffect(Angle(degrees: isLoading ? 360 : 0))
.animation(Animation.linear(duration: 1).repeatForever(autoreverses: false))
.onAppear() {
self.isLoading = true
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
加载指示器不必是圆形的。您也可以使用Rectangle或RoundedRectangle创建指标。但是您可以更改偏移量的值来创建动画,而无需更改旋转角度。
为了创建动画,我们将两个圆角矩形叠加在一起。上方的矩形比下方的矩形短得多。加载开始时,我们将其偏移值从-110更新为110。
import SwiftUI
struct ContentView: View {
@State private var isLoading = false
var body: some View {
ZStack {
Text("Loading")
.font(.system(.body, design: .rounded))
.bold()
.offset(x: 0, y: -25)
RoundedRectangle(cornerRadius: 3)
.stroke(Color(.systemGray5), lineWidth: 3)
.frame(width: 250, height: 3)
RoundedRectangle(cornerRadius: 3)
.stroke(Color.green, lineWidth: 3)
.frame(width: 30, height: 3)
.offset(x: isLoading ? 110 : -110, y: 0)
.animation(Animation.linear(duration: 1).repeatForever(autoreverses: false))
}
.onAppear() {
self.isLoading = true
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
这将使绿色矩形沿线移动。并且,当您一遍又一遍地重复相同的动画时,它将变成加载动画。下图说明了偏移值。