概览
SwiftUI 为我们来了界面设计和调试上的便利,只需几行代码我们就能实现一个不错的文本动画效果:
如上图所示,我们在 SwiftUI 中基本还没发力,就实现了文本摆动弹性动画。
这究竟是怎么做到的呢?
无需等待,Let‘s go!!! 😉
小小的推动
SwiftUI 中实现如上动画效果有很多种方法,比如:我们可以用状态改变驱动视图修改器的不同实参去产生动画效果。
我们也可以用文本的变体去实现类似的动画。
这里,我们混合使用它们来共同完成动画!
基本原则是:我们用 AttributedString 来设置文本的通用动画属性,而用 SwiftUI 视图修改器来实现文本的其它动画特性。
从 iOS 15.0(SwiftUI 3.0) 开始,Text 构造器开始支持 AttributedString 形参,我们可以直接创建 AttributedString 值并传入 Text 视图:
首先,在主视图加入动画所需的状态:
struct ContentView: View {
// 动画的启动状态
@State var anim = false
// 摆动的幅度
@State var range = 5.0
var body: some View {...}
}
接着,在主视图中构建一个 message 计算属性,不出意外它的类型是 AttributedString:
var message: AttributedString {
let string = "The letters go up and down"
var result = AttributedString()
for (index, letter) in string.enumerated() {
var letterString = AttributedString(String(letter))
// 利用一丢丢数学知识来计算文字上下偏移的位置
letterString.baselineOffset = sin(Double(index)) * range * (anim ? -1 : 1)
result += letterString
}
result.font = anim ? .largeTitle.weight(.black) : .largeTitle
result.foregroundColor = anim ? .red : .orange
return result
}
现在,我们可以把 message 放入 body 中的文本视图中了:
var body: some View {
VStack(spacing: 50) {
Spacer()
Text(message)
Spacer()
HStack {
Text(verbatim: "幅度:\(String(format: "%.1f", range))")
.font(.title.weight(.bold))
Slider(value: $range, in: 1.0...50)
}
.padding(.horizontal)
Button("动画吧!"){
withAnimation(.easeInOut.repeatForever(autoreverses: true)) {
anim.toggle()
}
}
// 省略无关外观代码...
}
}
运行看一下效果:
还不错,不是吗?😃
美化
欲穷千里目,更上一层楼,我们还可以在 SwiftUI 环境中进一步来优化动画显示。
别忘了,任何状态的改变在 SwiftUI 中都可以获得免费的动画效果,所以结合 message 已有的动画属性,我们可以利用修改器让文本动画更加生动:
Text(message)
.rotationEffect(anim ? .degrees(11) : .degrees(-11))
.fixedSize(horizontal: false, vertical: true)
.kerning(anim ? 1.7 : 0)
.brightness(anim ? 0.1 : 0.0)
.shadow(radius: 3.0)
如上代码所示,我们为 message 文本增加了诸多可动画修改器。最后,让我们再来观赏一下最终的成果吧:
至此,我们仅用很少的努力就实现了博文开头的动画效果,是不是觉得很赞呢?👍🏻💯
全部源代码
struct ContentView: View {
@State var anim = false
@State var range = 5.0
var message: AttributedString {
let string = "The letters go up and down"
var result = AttributedString()
for (index, letter) in string.enumerated() {
var letterString = AttributedString(String(letter))
letterString.baselineOffset = sin(Double(index)) * range * (anim ? -1 : 1)
result += letterString
}
result.font = anim ? .largeTitle.weight(.black) : .largeTitle
result.foregroundColor = anim ? .red : .orange
return result
}
var body: some View {
VStack(spacing: 50) {
Spacer()
Text(message)
.rotationEffect(anim ? .degrees(11) : .degrees(-11))
.fixedSize(horizontal: false, vertical: true)
.kerning(anim ? 1.7 : 0)
.brightness(anim ? 0.1 : 0.0)
.foregroundStyle(.thickMaterial)
.shadow(radius: anim ? 5.0 : 3.0)
Spacer()
HStack {
Text(verbatim: "幅度:\(String(format: "%.1f", range))")
.font(.title.weight(.bold))
Slider(value: $range, in: 1.0...50)
}
.padding(.horizontal)
Button("动画吧!"){
withAnimation(.easeInOut.repeatForever(autoreverses: true)) {
anim.toggle()
}
}
.font(.largeTitle.weight(.bold))
.foregroundStyle(.white.gradient)
.padding()
.padding(.horizontal)
.background(.blue.gradient)
.cornerRadius(7.0)
}
}
}
总结
在本篇博文中,我们充分发挥了 SwiftUI 框架简洁、富有描述性的威力,仅用几行代码就实现了一个不错的文本摆动弹性动画,而且可以自由调整参数实现各种变化。
更多 SwiftUI 中文本属性的操作和动画效果,请轻点如下链接观赏:
超详细:SwiftUI文本排版布局和动态字体缩放的奥秘
感谢观赏,再会!😎