ios 核心动画
Have you ever found yourself spending a lot of time playing around with an app just because it feels cool to use? Animations are for sure the reason why this happens. This is why they are so important and it’s why we need to dedicate a good amount of time in our development process to think about them.
您是否曾经因为使用感觉很酷而花大量时间在玩一个应用程序? 动画无疑是发生这种情况的原因。 这就是为什么它们如此重要的原因,也是为什么我们需要在开发过程中花大量时间思考它们。
Creating animations is one of the funniest parts of developing an app (or maybe the funniest part). It’s awesome to see what your imagination can come up with and then translate it into an actual application. Animations are also one of the key values that we need to deliver in order for our app’s users to stay engaged.
创建动画是开发应用程序中最有趣的部分之一(或者也许是最有趣的部分)。 看到您的想象力然后将其转化为实际的应用程序真是太棒了。 动画也是我们需要传递的关键价值之一,以便我们的应用程序用户保持参与度。
When we think about animations in Swift, we generally think about “UIView.animate” and that’s fairly enough in most cases. However, there are some scenarios in which we need more flexibility in order to achieve what we want. This is where Core Animations play their part.
当我们考虑Swift中的动画时,通常会考虑“ UIView.animate ”,在大多数情况下,这已经足够了。 但是,在某些情况下,我们需要更大的灵活性才能实现我们想要的目标。 这是核心动画发挥作用的地方。
In this article, we are going to learn how to create the following effects using mostly Core Animations
在本文中,我们将学习如何主要使用Core Animations来创建以下效果
演示地址
First thing we need to know is that while using “UIView.animate” animates views, Core Animations animates the properties of the layer. This means that any property of the CALayer class (and subclasses) can be animated. To check all the different properties that you can customize of a CABasicAnimation, you can check Apple’s Documentation
我们需要知道的第一件事是,在使用“ UIView.animate”对视图进行动画处理时,Core Animations对图层的属性进行了动画处理。 这意味着可以对CALayer类(和子类)的任何属性进行动画处理。 要检查您可以自定义CABasicAnimation的所有不同属性,可以检查Apple的文档
That being said, let’s jump into it!. In the example above, you for sure noticed the background color animation. Let’s start there. First, let’s create our gradient layer and add it to the view.
话虽如此,让我们跳进去吧!。 在上面的示例中,您肯定注意到了背景色动画。 让我们从这里开始。 首先,让我们创建渐变层并将其添加到视图中。
let colorOne = UIColor(red: 0, green 157/255, 248/255, alpha: 1).cgColor let colorTwo = UIColor(red: 206/255, green 7/255, 85/255, alpha: 1).cgColor let colorThree = UIColor(red: 245/255, green 180/255, 51/255, alpha: 1).cgColor var gradientColors = [[]]() gradientColors.append([colorOne, colorTwo]) gradientColors.append([colorTwo, colorThree]) gradientColors.append([colorThree, colorOne]) gradientColors.append([colorOne, colorThree]) var currentGradient = 0 let gradient = CAGradientLayer() gradient.frame = view.bounds gradient.colors = gradientColors[currentGradient] gradient.startPoint = CGPoint(x:0, y:0) gradient.endPoint = CGPoint(x:1, y:1) gradient.drawsAsynchronously = true view.layer.insertSublayer(gradientLayer, at: 0)Then, we need to create our Core Animation. This can be done using the initializer function:
然后,我们需要创建我们的核心动画。 这可以使用初始化函数来完成:
let gradientAnimation = CABasicAnimation(keyPath: #keyPath(CAGradientLayer.colors))You may have also noticed that the CABasicAnimation initializer request passes a string as a parameter. This string corresponds to the property that you’re going to animate.
您可能还注意到,CABasicAnimation初始化程序请求将字符串作为参数传递。 该字符串对应于您要设置动画的属性。
You might be thinking, why is this parameter a string instead of an enum value? Well, CoreAnimations were originally written in Objective-C and as you might know, enums are not supported as they are in Swift. However, there’s a trick that we can implement in order not to use string literals. Instead, we can use #keyPath compiler command and turn the property that we want to animate type safe. (Note that not all properties have their keyPath equivalent, there will be times when we will have no choice but to use a literal String)
您可能在想,为什么此参数是字符串而不是枚举值? 好吧,CoreAnimations最初是用Objective-C编写的,您可能知道,不支持枚举,就像Swift中那样。 但是,我们可以实现一个技巧,以不使用字符串文字。 相反,我们可以使用#keyPath编译器命令,然后将要设置动画的属性设置为安全。 (请注意,并非所有属性都具有等效的keyPath,有时我们别无选择,只能使用文字字符串)
Now that the initialization is completed, we can configure our animations.
现在初始化已完成,我们可以配置动画了。
gradientAnimation.duration = 3.0 // 1 gradientAnimation.toValue = gradientColors[currentGradient] // 2 gradientAnimation.fillMode = .forwards // 3 gradientAnimation.isRemovedOnCompletion = false // 4 gradientLayer.add(gradientAnimation, forKey: "gradientChangeAnimation") // 5 The duration of the animation.动画的持续时间。This represents the values that we are going to interpolate between. Basically, the animation that we want to get. Another property that you can set here is fromValue. If you don’t set it, then the interpolation is going to be between the current value of keyPath in the target layer’s presentation and the toValue property. 这代表了我们将要插值的值。 基本上,就是我们想要获得的动画。 您可以在此处设置的另一个属性是fromValue。 如果未设置,则插值将介于目标层表示中keyPath的当前值和toValue属性之间。 The fill mode, in this case we set it to forwards. 填充模式,在这种情况下,我们将其设置为正向。 With this we assure that the animations will be start again when it ends 这样,我们确保动画将在结束时重新开始 We add the animation to our gradient layer.我们将动画添加到渐变层。Finally, we need the color change for when the animation completes. To do this, we need to implement the CAAnimationDelegate of our animation. Let’s also encapsulate our animation logic into a function and update the currentGradient index.
最后,我们需要在动画完成时更改颜色。 为此,我们需要实现动画的CAAnimationDelegate。 让我们还将动画逻辑封装到一个函数中,并更新currentGradient索引。
private func animateGradient() { currentGradient = currentGradient < gradientColors.count - 1 ? currentGradient + 1 : 0 gradientAnimation = CABasicAnimation(keyPath: #keyPath(CAGradientLayer.colors)) gradientAnimation.duration = 3.0 gradientAnimation.toValue = gradientColors[currentGradient] gradientAnimation.fillMode = .forwards gradientAnimation.isRemovedOnCompletion = false gradientAnimation.delegate = self gradientLayer.add(gradientAnimation, forKey: "gradientChangeAnimation") } // CAAnimationDelegate func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { if flag { gradientLayer.colors = gradientColors[currentGradient] animateGradient() } }With this in place, we now have our background animation! Let’s now dive into the label animation. We can divide it into two separate animations, one that zooms in, stops for a little and then continues zooming in. And another one that changes the opacity when it reaches the highest zoom level.
有了这个,我们现在有了背景动画! 现在让我们进入标签动画。 我们可以将其分为两个单独的动画,一个放大,停止一小段然后继续放大。另一个动画在达到最高缩放级别时更改不透明度。
Let’s go ahead and create the animation for the first one.
让我们继续创建第一个动画。
let scaleAnimation = CABasicAnimation(keyPath: "transform.scale") scaleAnimation.toValue = 6 scaleAnimation.duration = 1 scaleAnimation.isRemovedOnCompletion = trueWe are now going to see one of the most powerful features Core Animations provides. As you might know, with UIKit, there are some predefined timing functions that we can use such as easeInOut for example. However, with Core Animations, we can define custom ones.
现在,我们将看到Core Animations提供的最强大的功能之一。 您可能知道,对于UIKit,我们可以使用一些预定义的计时功能,例如easyInOut。 但是,使用Core Animations,我们可以定义自定义动画。
scaleAnimation.timingFunction = CAMediaTimingFunction(controlPoints: 0.11, 0.9, 0.92, 0.13)Don’t worry, you don’t have to deal with this math, you can play around in the following link: https://cubic-bezier.com/. It will give you the values of your timing function once you’re done.
不用担心,您不必处理这个数学问题,可以在以下链接中进行尝试: https : //cubic-bezier.com/ 。 完成后,它将为您提供计时功能的值。
Now that this is completed, we are now going to focus on the second one.
现在已经完成了,我们现在将集中在第二个上。
let opacityAnimation = CABasicAnimation(keyPath: "opacity") opacityAnimation.toValue = 0 opacityAnimation.duration = 0.5 opacityAnimation.beginTime = CACurrentMediaTime() + 0.5 // With this line we add a delayHaving both animations implemented, let’s put the pieces together. In order to be able to show the countdown, a label, an array that holds the countdown values, and a variable that keeps the current value to display are needed.
实现两个动画后,让我们将各个部分放在一起。 为了能够显示倒计时,需要一个标签,一个包含倒计时值的数组以及一个将当前值显示的变量。
var currentCountDownStep = -1 { didSet { if currentCountDownStep >= 0 { if currentCountDownStep < countDownTexts.count { countDownLabel.text = countDownTexts[currentCountDownStep] animateCountDownLabel() } else { currentCuntDownStep = -1 } } } } let countDownTexts = ["Ready?", "3", "2", "1", "Go!"] func animateCountDownLabel() { setupScaleAnimation() setupOpacityAnimation() } func setupScaleAnimation() { let scaleAnimation = CABasicAnimation(keyPath: "transform.scale") scaleAnimation.toValue = 6 scaleAnimation.duration = 1 scaleAnimation.isRemovedOnCompletion = true scaleAnimation.timingFunction = CAMediaTimingFunction(controlPoints: 0.11, 0.9, 0.92, 0.13) countDownLabel.layer.add(scaleAnimation, forKey: "ScaleCountDownAnimation") } func setupOpacityAnimation() { let opacityAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity)) opacityAnimation.toValue = 0 opacityAnimation.duration = 0.5 opacityAnimation.beginTime = CACurrentMediaTime() + 0.5 countDownLabel.layer.add(opacityAnimation, forKey: "OpacityCountDownAnimation") }The only missing part is that we need to change currentCountDownStep once the animation is completed. This needs to be done in the CAAnimationDelegate. However, we already used it for the background color animation and we don’t have a reference in the function parameters to know when the animation completed.
唯一缺少的部分是动画完成后,我们需要更改currentCountDownStep。 这需要在CAAnimationDelegate中完成。 但是,我们已经将其用于背景色动画,并且在函数参数中没有引用来知道动画何时完成。
Well, don’t worry, we’ve a little trick for that. We can add custom properties to our animations and then retrieve them in the delegate method. In this particular case, we can add some custom “id” to our animations and perform some actions based on it.
好吧,不用担心,为此我们有一些技巧。 我们可以将自定义属性添加到动画中,然后在委托方法中对其进行检索。 在这种特殊情况下,我们可以向动画中添加一些自定义“ id”,并根据其执行一些操作。
let animationId = "id" let gradientAnimationId = "GradientAnimation" let scaleAnimationId = "ScaleAnimation" gradientAnimation.setValue(gradientAnimationId, forKey: animationId) scaleAnimation.setValue(scaleAnimationId, forKey: animationId) func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { guard let animation = anim.value(forKey: animationId) as? String else { return } if flag { if animation == gradientAnimationId { gradientLayer.colors = gradientColors[currentGradient] animateGradient() } else if animation == scaleAnimationId { currentCountDownStep += 1 } } }And that’s all you need. With literally three basic core animations you can achieve some pretty cool effects!
这就是您所需要的。 使用字面上的三个基本核心动画,您可以实现一些非常酷的效果!
We currently live in the SwiftUI era. However, UIKit is going to be around for a few more years so adding these concepts to your skill set will for sure be of great benefit.
我们目前生活在SwiftUI时代。 但是,UIKit将继续存在数年,因此将这些概念添加到您的技能组合中肯定会带来很大的好处。
You can check the full project here
您可以在此处查看完整的项目
If you’ve any questions or comments please feel free to reach me on twitter: b_lorenzo10 . I’ll be happy to chat =)
如果您有任何疑问或意见,请随时通过twitter: b_lorenzo10与我联系。 我很乐意聊天=)
Cheers!
干杯!
翻译自: https://medium.com/swlh/improve-your-ux-with-core-animations-d1f7677cdb86
ios 核心动画
相关资源:四史答题软件安装包exe