swiftui
When using SwiftUI, it’s important to animate views and transitions with silky smooth performance. This article introduces the two protocols for SwiftUI animation that every developer must know.
使用SwiftUI时,以柔滑流畅的效果为视图和过渡设置动画非常重要。 本文介绍了每个开发人员都必须知道的两种SwiftUI动画协议。
1.动画协议 (1. The Animatable Protocol)
The Animatable
protocol is available on iOS 13+. It defines the type that can be animated. The only value the protocol contains is animatableData
which is the associated type of VectorArithmetic
. VectorArithmetic
extends the AdditiveArithmetic
protocol with scalar multiplication and a way to query the vector magnitude of the value. The Animatable
protocol helps us in animating paths and transforming matrices.
Animatable
协议可在iOS 13+上使用。 它定义了可以动画化的类型。 该协议包含的唯一值是animatableData
它是相关联的类型的VectorArithmetic
。 VectorArithmetic
通过标量乘法扩展了AdditiveArithmetic
协议,并提供了一种查询值的矢量幅度的方法。 Animatable
协议可以帮助我们动画化路径和转换矩阵。
/// A type that can be animated
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol Animatable {
/// The type defining the data to be animated.
associatedtype AnimatableData : VectorArithmetic
/// The data to be animated.
var animatableData: Self.AnimatableData { get set }
}
SwiftUI traverses the view hierarchy to find the values adopt Animatable
protocol and animate its changes by using the animatableData
of a particular item.
SwiftUI遍历视图层次结构以查找采用Animatable
协议的值,并通过使用特定项目的animatableData对其变化进行animatableData
处理。
Let’s explain it more by using the progress circle animation as an example.
让我们以进度圈动画为例进行更多说明。
State
变量 (State
variables)
Usually, we use the @State
property wrapper to define some state variables and then attach the animation modifier to the view. The progress
is the state variable of the progress by percent. And we use the instance of Circle
to draw the progress arc.
通常,我们使用@State
属性包装器定义一些状态变量,然后将animation修饰符附加到视图。 progress
是progress
的状态变量(按百分比计)。 然后,我们使用Circle
实例绘制进度弧。
struct ContentView: View {
@State private var progress: CGFloat = 0.0
var body: some View {
ZStack {
Circle() // Inactive
.stroke(lineWidth: 6)
.frame(width: 90, height: 90)
.foregroundColor(Color(.systemGray6))
Rectangle()
.frame(width: 24, height: 24)
.foregroundColor(Color(.systemIndigo))
.cornerRadius(2)
Circle()
.trim(from: 0, to: progress)
.stroke(style: StrokeStyle(lineWidth: 6, lineCap: .round, lineJoin: .round))
.frame(width: 90, height: 90)
.foregroundColor(Color(.systemIndigo))
.rotationEffect(.degrees(-90))
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 0, z: 0))
.animation(Animation.easeInOut.speed(0.25))
.onAppear() {
self.progress = 1
}
}
}
}
动画和动画数据 (Animatable and AnimatableData)
To understand the details of the Animatable
, let’s take a look at the following example.
要了解Animatable
的细节,让我们看下面的示例。
struct AnimatableCircle: Shape {
var progress: CGFloat
func path(in rect: CGRect) -> Path {
let centerX: CGFloat = rect.width / 2
let centerY: CGFloat = rect.height / 2
var path = Path()
path.addArc(center: CGPoint(x: centerX, y: centerY),
radius: 45,
startAngle: Angle(degrees: -90),
endAngle: Angle(degrees: 360 * Double(progress)),
clockwise: false)
return path
}
}
struct ContentView: View {
@State private var progress: CGFloat = 0.0
var body: some View {
ZStack {
Circle()
.stroke(lineWidth: 6)
.frame(width: 90, height: 90)
.foregroundColor(Color(.systemGray6))
Rectangle()
.frame(width: 24, height: 24)
.foregroundColor(Color(.systemIndigo))
.cornerRadius(2)
AnimatableCircle(progress: progress)
.stroke(style: StrokeStyle(lineWidth: 6, lineCap: .round, lineJoin: .round))
.frame(width: 90, height: 90)
.foregroundColor(Color(.systemIndigo))
.animation(Animation.easeInOut.speed(0.25))
.onAppear() {
self.progress = 1
}
}
}
}
Here we have a AnimatableCircle
that adopts the Shape
protocol. All shapes in SwiftUI conform to the Animatable
protocol, but there is no animation while launching the example.
在这里,我们有一个采用Shape
协议的AnimatableCircle
。 在SwiftUI所有形状符合Animatable
的协议,但没有动画同时发动的例子。
SwiftUI doesn’t identify how to animate the circle as there is no AnimatableData
.
由于没有AnimatableData,SwiftUI无法确定如何为圆形设置AnimatableData
。
struct AnimatableCircle: Shape {
var progress: CGFloat
var animatableData: CGFloat {
get { progress }
set { progress = newValue }
}
func path(in rect: CGRect) -> Path {
let centerX: CGFloat = rect.width / 2
let centerY: CGFloat = rect.height / 2
var path = Path()
path.addArc(center: CGPoint(x: centerX, y: centerY),
radius: 45,
startAngle: Angle(degrees: -90),
endAngle: Angle(degrees: 360 * Double(progress)),
clockwise: false)
return path
}
}
By implementing the animatableData
, we update the progress
property and can animate it. On top of that, by implementing the Shape
protocol, it gives us more flexibility to do more customizable animation.
通过实现animatableData
,我们可以更新progress
属性并可以对其进行动画处理。 最重要的是,通过实现Shape
协议,它为我们提供了更大的灵活性来执行更多可定制的动画。
2. AnimatableModifier协议 (2. The AnimatableModifier Protocol)
The AnimatableModifier
protocol adopts the Animatable
and ViewModifier
protocols. It’s available on iOS 13+. As you may have already noticed from the protocol’s name, it can animate the path of the Shape
and also the content of the View
!
所述AnimatableModifier
协议采用Animatable
和ViewModifier
协议。 在iOS 13+上可用。 正如您可能已经从协议名称中注意到的那样,它可以为Shape
的路径以及View
的内容设置动画!
/// A modifier that can animatedly create another modifier.
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol AnimatableModifier : Animatable, ViewModifier {
}
Let’s explain it by using the progress circle animation with the percent label as an example.
让我们以带有百分比标签的进度圈动画为例进行说明。
struct AnimatableCircle: Shape {
var progress: CGFloat
func path(in rect: CGRect) -> Path {
let centerX: CGFloat = rect.width / 2
let centerY: CGFloat = rect.height / 2
var path = Path()
path.addArc(center: CGPoint(x: centerX, y: centerY),
radius: rect.width / 2,
startAngle: Angle(degrees: -90),
endAngle: Angle(degrees: 360 * Double(progress)),
clockwise: false)
return path.strokedPath(.init(lineWidth: 6, lineCap: .round, lineJoin: .round))
}
}
struct TextView: View {
var progress: CGFloat = 0
var body: some View {
Text("\(Int(progress * 100)) %")
.font(.headline)
.foregroundColor(Color(.systemIndigo))
.transition(.opacity)
.id(progress.description)
}
}
struct PercentageAnimatableCircle: AnimatableModifier {
var progress: CGFloat = 0
var animatableData: CGFloat {
get { progress }
set { progress = newValue }
}
func body(content: Content) -> some View {
content
.foregroundColor(Color(.systemBackground))
.overlay(AnimatableCircle(progress: progress).foregroundColor(Color(.systemIndigo)))
.overlay(TextView(progress: progress))
}
}
Let’s go through the code one by one:
让我们一步一遍地检查代码:
可动画圆 (AnimatableCircle)
The AnimatableCircle
adopts the Shape
protocol to draw the arc by implementing the path function.
AnimatableCircle
采用Shape
协议,通过实现path函数来绘制圆弧。
We didn’t make AnimatableCircle
animatable nor use the animatableData
.
我们没有将AnimatableCircle
animable,也没有使用animatableData
。
文字检视 (TextView)
We added the instance of Text
with the percentage in the view. It implements the body of View
but didn’t use @State
or animatableData
either.
我们在视图中添加了带有百分比的Text
实例。 它实现的体View
而没有使用@State
或animatableData
无论是。
It is worth it to note that SwiftUI uses the additional .id
at the end of the Text
to decide if it’s dealing with the same view or a new one. By using a unique id, SwiftUI will redraw it with the specified transition as expected.
值得注意的是,SwiftUI在Text
的末尾使用了附加的.id
来确定它是在处理相同视图还是新视图。 通过使用唯一的ID,SwiftUI将按预期的指定过渡进行重绘。
动画修饰符 (AnimatableModifier)
The PercentageAnimatableCircle
adopts AnimatableModifier
. As the AnimatableModifier
conforms to Animatable
and ViewModifier
, we now have two places to do the animation: animatableData
and body(content: Content)
.
PercentageAnimatableCircle
采用AnimatableModifier
。 由于AnimatableModifier
符合Animatable
和ViewModifier
,我们现在有两个地方做动画: animatableData
和body(content: Content)
。
The animatableData
updates the progress percent and the body
controls the content overlays.
animatableData
更新进度百分比,并且body
控制内容叠加层。
结论 (Conclusion)
The article introduces the two most important protocols, Animatable
and AnimatableModifier
, for SwiftUI animation. By adopting them, we can achieve many exciting animations and leverage the UX of our app.
本文介绍了两个最重要的协议, Animatable
和AnimatableModifier
,为SwiftUI动画。 通过采用它们,我们可以实现许多激动人心的动画并利用我们应用的用户体验。
翻译自: https://medium.com/better-programming/2-must-know-protocols-for-swiftui-animation-cd50bf38895e
swiftui