自从2001年Mac OS X推出以来,史蒂夫·乔布斯(Steve Jobs)推出了Aqua这个视觉主题,而该主题一直为macOS提供动力,他说:“我们使屏幕上的按钮看起来好得让人想要舔它们。”我不知道是否你在那时使用Macs,但是多年来,Aqua一直给我们提供类似玻璃的按钮,大头针条纹,拉丝金属等等,甚至到今天,“精灵”窗口也将外观最小化。

当我们制作具有出色视觉吸引力的应用程序时,用户会注意到。当然,它不会影响应用程序的核心功能,很容易过分设计,导致该核心有点丢失,但是当你正确操作时,漂亮的用户界面会带来一点额外的乐趣,并且可以帮助你将你的应用与众不同。

关于 CGFloat

由于历史原因,主要是与Apple的旧API交互的原因,我们需要使用一种称为的特定数据类型CGFloat。

CGFloat出于各种意图和目的,它是Double一个不同的名称,但在较旧的硬件上,它使用的数字存储类型较小,称为Float。当这个选择很重要时,CGFloatApple不必在乎我们要为哪种类型的硬件建造,但如今几乎所有东西都在使用,Double因此,厌恶地盯着我们只是一小块遗产。

无论如何,所有这些都很重要,因为如果我们使该属性var animationAmount = 1得到一个整数,并且如果我们使用它,var animationAmount = 1.0那么我们得到一个Double,但是没有内置的方法来CGFloat自动获得–我们需要使用类型注释。

因此,请立即将此属性添加到你的视图中:

1
@State private var animationAmount: CGFloat = 1

基本隐式动画

通过改变状态的值来操控组件的状态,并在结尾添加.animation(.default)来添加动画:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import SwiftUI

struct ContentView: View {
@State private var animationAmount: CGFloat = 1
var body: some View {
Button("button") {
self.animationAmount += 1
}
.padding(50)
.background(Color.red)
.clipShape(Circle())
.foregroundColor(.white)
.scaleEffect(animationAmount)
.blur(radius: (animationAmount - 1) * 3)
.animation(.default)
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

动画样式:加速开始,缓慢结束

我们可以.easeOut用来使动画快速开始,然后减速至平稳停止:

1
.animation(.easeOut)

动画样式:弹性动画

弹簧动画,这些动画会导致运动过冲,然后返回以稳定其目标。你可以控制弹簧的初始刚度(在动画开始时设置其初始速度),还可以控制动画“阻尼”的速度–较低的值会使弹簧来回弹跳更长的时间。

例如,这使我们的按钮快速放大然后反弹:

1
.animation(.interpolatingSpring(stiffness: 50, damping: 1))

设置动画持续时间

为了进行更精确的控制,我们可以使用指定的持续时间(以秒为单位)自定义动画。因此,我们可以获得一个持续两秒钟的缓入动画,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct ContentView: View {
@State private var animationAmount: CGFloat = 1

var body: some View {
Button("Tap Me") {
self.animationAmount += 1
}
.padding(50)
.background(Color.red)
.foregroundColor(.white)
.clipShape(Circle())
.scaleEffect(animationAmount)
.animation(.easeInOut(duration: 2))
}
}

设置动画延迟

当我们说.easeInOut(duration: 2)我们实际上是在创建Animation具有自己的修饰符集的结构实例时。因此,我们可以将修改器直接附加到动画上以添加如下所示的延迟:

1
2
3
4
.animation(
Animation.easeInOut(duration: 2)
.delay(1)
)

你会注意到我们Animation.easeInOut()现在必须明确地说,因为否则Swift不太清楚我们的意思。无论如何,点击按钮现在将等待一秒钟,然后执行两秒钟的动画。

反复动画

1
2
3
4
.animation(
Animation.easeInOut(duration: 1)
.repeatForever(autoreverses: true)
)

永远重复动画

对于连续动画,repeatForever()可以使用如下修饰符:

1
2
3
4
.animation(
Animation.easeInOut(duration: 1)
.repeatForever(autoreverses: true)
)

脉冲形式

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
33
34
35
import SwiftUI

struct ContentView: View {
@State private var animationAmount: CGFloat = 1
var body: some View {
Button("button") {
// self.animationAmount += 1
}
.padding(50)
.background(Color.red)
.clipShape(Circle())
.foregroundColor(.white)
.overlay(
Circle()
.stroke(Color.red)
.scaleEffect(animationAmount)
.opacity(Double(2 - animationAmount))
.animation(
Animation.easeOut(duration: 1)
.repeatForever(autoreverses: false)
)
)
.onAppear {
self.animationAmount = 2
}

}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

在步进器中插入代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct ContentView: View {
@State private var animationAmount: CGFloat = 1

var body: some View {
VStack {
Stepper("Scale amount", value: $animationAmount.animation(), in: 1...10)

Spacer()

Button("Tap Me") {
self.animationAmount += 1
}
.padding(40)
.background(Color.red)
.foregroundColor(.white)
.clipShape(Circle())
.scaleEffect(animationAmount)
}
}
}

因为按钮和步进器绑定了相同的状态,所以他们都能控制。并且因为步进器插入了动画,我们甚至能看到点击步进器会发生流畅的动画,而在点击按钮时不会有。

我们还可以将步进器的代码的动画部分进行扩写:

1
2
3
4
Stepper("Scale amount", value: $animationAmount.animation(
Animation.easeInOut(duration: 1)
.repeatCount(3, autoreverses: true)
), in: 1...10)

状态的值随着动画更改

进行这项工作需要我们可以修改的某些状态,并且旋转度指定为Double。因此,请立即添加此属性:

1
@State private var animationAmount = 0.0

接下来,我们将要求按钮animationAmount沿其Y轴旋转角度,这意味着它将向左和向右旋转。现在将此修饰符添加到按钮:

1
.rotation3DEffect(.degrees(animationAmount), axis: (x: 0, y: 1, z: 0))

现在是重要部分:我们将在按钮的动作中添加一些代码,以便在animationAmount每次点击时将其添加360 。

如果我们只写,self.animationAmount += 360那么更改将立即发生,因为按钮上没有附加动画修改器。这是显式动画出现的地方:如果我们使用withAnimation()闭包,那么SwiftUI将确保由新状态引起的任何更改都将自动进行动画处理。

因此,现在将其放入按钮的操作中:

1
2
3
withAnimation {
self.animationAmount += 360
}

在旋转时应用弹性动画

withAnimation()可以使用可以在SwiftUI中其他位置使用的所有相同动画来赋予动画参数。例如,我们可以使用如下withAnimation()调用来使旋转效果使用spring动画:

1
2
3
withAnimation(.interpolatingSpring(stiffness: 5, damping: 1)) {
self.animationAmount += 360
}

参考资料

查看下一天的SwiftUI学习笔记

关于100days英文课程