swiftui_SwiftUI动画的2个必备知识协议

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
Photo by Simon Berger on Unsplash
Simon BergerUnsplash拍摄的照片

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它是相关联的类型的VectorArithmeticVectorArithmetic通过标量乘法扩展了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.

让我们以进度圈动画为例进行更多说明。

Progress Circle Animation by Eric Yang
Progress circle animation by Eric Yang
杨ric的进度圈动画

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修饰符附加到视图。 progressprogress的状态变量(按百分比计)。 然后,我们使用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
                }
        }
    }
}
Non-Animatable Circle By Eric Yang
Non-animatable circle by Eric Yang
不可动画的圈子,作者:Eric Yang

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
    }
}
Progress Circle Animation by Eric Yang
Animatable Circle by Eric Yang
动画圈,埃里克·杨(Eric Yang)

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
Photo by Fabian Mardi on Unsplash
Fabian MardiUnsplash拍摄的照片

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协议采用AnimatableViewModifier协议。 在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.

让我们以带有百分比标签的进度圈动画为例进行说明。

Progress Circle With Percent Animation by Eric Yang
Progress circle with percent animation by Eric Yang
动画进度圈,作者:Eric Yang
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而没有使用@StateanimatableData无论是。

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符合AnimatableViewModifier ,我们现在有两个地方做动画: animatableDatabody(content: Content)

The animatableData updates the progress percent and the body controls the content overlays.

animatableData更新进度百分比,并且body控制内容叠加层。

结论 (Conclusion)

Conclusion
Photo by Aaron Burden on Unsplash
照片由 Aaron BurdenUnsplash拍摄

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.

本文介绍了两个最重要的协议, AnimatableAnimatableModifier ,为SwiftUI动画。 通过采用它们,我们可以实现许多激动人心的动画并利用我们应用的用户体验。

资源资源 (Resources)

All code mentioned above can be found at the Gist repo.

上面提到的所有代码都可以在Gist仓库中找到。

翻译自: https://medium.com/better-programming/2-must-know-protocols-for-swiftui-animation-cd50bf38895e

swiftui

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值