【译】用UIKit和UIView在视图上执行iOS动画

本文旨在成为iOS动画的入门读物,目的是详尽地介绍不同的实现方法。

鉴于该主题的广泛性,我们将在相当高的层次上简洁地涵盖每个部分。这样做的目的是通过一组选项来教育读者将动画添加到他/她的iOS应用程序中。

在我们开始讨论与IOS相关的主题之前,让我们先简单地看一看动画的速度。

60 Fps动画

通常,在视频中,每一帧由图像表示,帧速率决定在序列中翻转的图像数量。这被称为“帧每秒”或FPS。

FPS确定在一秒钟内翻转的静止图像的数量,这实际上意味着图像/帧的数量越多,视频中显示的细节/信息就越多。这也适用于动画。

FPS通常用于确定动画的质量。有一种流行的观点认为,任何好的动画应该运行在60英尺或更高-任何低于60 fps将感到有点不对劲。

你想看看30 FPS和60 FPS之间的区别吗? 看看这个 !

你注意到区别了吗?人的眼睛肯定能感觉到低fps的抖动。因此,确保您所创建的任何动画都遵循运行在60 fps或更高的基本规则,这是一个很好的实践。这让它感觉更现实,更有活力。

在查看了FPS之后,现在让我们深入研究不同的核心iOS框架,这些框架为我们提供了一种执行动画的方法

核心框架

在本节中,我们将讨论可以用于创建视图动画的IOSSDK中的框架。我们将对它们进行快速的浏览,并以相关的示例说明它们的特性集。<

UIKIT/UIVIEW动画

UIView是任何在iOS应用程序中显示内容的视图的基类。

UIKit是为我们提供UIView的框架,它已经为我们提供了一些基本的动画功能,使得开发人员可以通过更少的操作来实现更多的目标。

API UIView.animate ,因为通过提供基于块的语法中的属性值,任何视图的属性都可以很容易地被动画化。

在UIKit动画中,建议只修改UIVIew的可动画属性,否则动画可能会导致视图处于意外状态。

动画(附图:动画:完成)

此方法接受动画持续时间,这是一组需要动画化的视图的可动画属性更改。完成块在视图执行动画时提供回调。

几乎任何类型的动画,如移动,缩放,旋转,褪色,等等,在一个视图可以实现这个单一的API。

现在,考虑您想要动画一个按钮大小的变化,或者您想要一个特定的视图放大到屏幕。这就是我们如何使用 UIView.animate API:

let newButtonWidth: CGFloat = 60

UIView.animate(withDuration: 2.0) { //1
    self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth) //2
    self.button.center = self.view.center //3
}
复制代码

我们在这里做的是:

UIView.animate
center

上面的动画代码块应该触发按钮框架的动画,而不是当前的框架:

Width = 0, Height = 0

最后一个框架:

Width = Height = newButtonWidth

下面是动画的样子:

animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion

此方法类似于动画方法的扩展,您可以在前面的API中执行所有可以执行的操作,并将一些物理行为添加到视图动画中。

例如,如果您想在上面所做的动画中实现弹簧阻尼效果,那么代码如下所示:

let newButtonWidth: CGFloat = 60
UIView.animate(withDuration: 1.0, //1
    delay: 0.0, //2
    usingSpringWithDamping: 0.3, //3
    initialSpringVelocity: 1, //4
    options: UIView.AnimationOptions.curveEaseInOut, //5
    animations: ({ //6
        self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth)
        self.button.center = self.view.center
}), completion: nil)
复制代码

下面是我们使用的一组参数:

duration
delay
SpringWithDamping
velocity
options

下面是用上面的动画配置动画的样子:

UIViewProperty动画

为了更好的控制动画, UIViewPropertyAnimator 它为我们提供了暂停和恢复动画的方法。您可以有自定义的定时,并使您的动画具有交互性和可中断性。这在执行动画时非常有用,这些动画也可以与用户操作交互。

经典的“滑动解锁”手势和播放器视图“解散/扩展动画”(在音乐应用程序中)是交互式动画和可中断动画的例子。您可以开始用手指移动视图,然后释放它,视图将回到原来的位置。或者,您可以在动画期间捕捉视图并继续用手指拖动视图。

下面是一个简单的示例,说明如何使用 UIViewPropertyAnimator :

let newButtonWidth: CGFloat = 60
let animator = UIViewPropertyAnimator(duration:0.3, curve: .linear) { //1
    self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth)
    self.button.center = self.view.center
}
animator.startAnimation() //2
复制代码

我们正在做的事情如下:

UIViewProperty

现在,让我们假设你想要更多的控制动画。例如,您希望设计和控制动画中的每个帧。还有另一个API animateKeyframes 。但是在我们深入研究它之前,让我们快速地看看一个框架是什么,在一个动画中。

什么是A frame ?

视图的框架更改/转换集合(从开始状态到最终状态)定义为 animation 动画期间视图的每个位置都被称为 frame .

动画关键帧

这个API提供了一种设计动画的方法,使您可以定义具有不同时间和转换的多个动画。发布这篇文章后,API简单地将所有动画集成到一个无缝体验中。

假设我们想以随机的方式移动屏幕上的按钮。让我们看看如何使用KeyFrame动画API来做到这一点。

UIView.animateKeyframes(withDuration: 5, //1
  delay: 0, //2
  options: .calculationModeLinear, //3
  animations: { //4
    UIView.addKeyframe( //5
      withRelativeStartTime: 0.25, //6
      relativeDuration: 0.25) { //7
        self.button.center = CGPoint(x: self.view.bounds.midX, y: self.view.bounds.maxY) //8
    }

    UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.25) {
        self.button.center = CGPoint(x: self.view.bounds.width, y: start.y)
    }

    UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 0.25) {
        self.button.center = start
    }
})
复制代码

详细情况如下:

duration
delay
options
animations
addKeyFrame
relativeStartTime
relativeDuration
center

最后的动画是这样的:

共动画

任何基于UIKit的动画都是在内部转换成核心动画。因此,核心动画框架充当任何UIKit动画的支持层或骨干。因此,所有UIKit动画API都只是以一种易于消费或方便的方式封装了核心动画API的层。

UIKit动画API不提供对视图执行的动画的太多控制,因为它们主要用于视图的可动画属性。因此,在这种情况下,如果您想要控制动画的每一个帧,最好直接使用底层的核心动画API。或者,UIView动画和核心动画也可以一起使用。

UIView+核心动画

让我们看看如何重新创建相同的按钮更改动画,同时使用UIView和Core动画API指定时间曲线。

我们可以用 CATransaction 的定时功能,它允许您指定和控制动画曲线。

让我们来看一个按钮大小变化动画的例子,它的角半径使用 CATransaction 的定时功能和UIView动画的组合:

let oldValue = button.frame.width/2
let newButtonWidth: CGFloat = 60

/* Do Animations */
CATransaction.begin() //1
CATransaction.setAnimationDuration(2.0) //2
CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)) //3

// View animations //4
UIView.animate(withDuration: 1.0) {
    self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth)
    self.button.center = self.view.center
}

// Layer animations
let cornerAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.cornerRadius)) //5
cornerAnimation.fromValue = oldValue //6
cornerAnimation.toValue = newButtonWidth/2 //7

button.layer.cornerRadius = newButtonWidth/2 //8
button.layer.add(cornerAnimation, forKey: #keyPath(CALayer.cornerRadius)) //9

CATransaction.commit() //10
复制代码

详细情况如下:

  1. begin 表示动画代码块的开始。
  2. duration 整体动画持续时间。
  3. curve 表示需要应用于动画的计时曲线。
  4. UIView.animate 我们的第一个动画改变框架的按钮。
  5. CABasicAnimation 我们创建 CABasicAnimation 引用 cornerRadius 按钮作为键盘,因为这是我们想要的动画。类似地,如果希望对关键帧动画具有粒度级控制,则可以使用 CAKeyframeAnimation 班级。
  6. fromValue 表示动画的起始值,即 cornerRadius 从动画必须从哪里开始的按钮的值。
  7. toValue 表示动画的最终值,即最终值。 cornerRadius 动画必须结束的按钮的值。
  8. cornerRadius 我们必须设置`cornerRadius属性的属性,以动画的最终值,否则,按钮的角半径值将在动画完成后自动恢复到其初始值。
  9. addAnimation 通过表示动画需要执行的Keypath,我们将包含整个动画过程配置的动画对象附加到该层。
  10. commit 表示动画代码块的结束并开始动画。

最后的动画是这样的:

这个博客 是一个伟大的阅读,以帮助创建更高级的动画,因为它整齐地带您通过大多数核心动画框架API指导您通过每一步的道路。

UIKITDYNAMICS

UIKitDynamic是UIKit的物理引擎,它使您能够在UIKit控件中添加任何物理行为,如碰撞、重力、推、扣等。

UIKitDynamic动画

这是UIKitDynamicyFramework的管理类,它规范由任何给定UI控件触发的所有动画。

UIKitDynamicBehavior

它使您可以将任何物理行为添加到动画师中,从而使其能够在附在其上的视图上执行操作。

UIKitDynamic的各种行为包括:

UIAttachmentBehavior
UICollisionBehavior
UIFieldBehavior
UIGravityBehavior
UIPushBehavior
UISnapBehavior

UIKitDynamic的体系结构类似于 。请注意,项目1至5可以替换为单个视图。

让我们把一些物理行为应用到我们的按钮上。我们将看到如何将重力应用到按钮上,这样它就能给我们一种处理真实物体的感觉。

var dynamicAnimator   : UIDynamicAnimator!
var gravityBehavior   : UIGravityBehavior!

dynamicAnimator = UIDynamicAnimator(referenceView: self.view) //1

gravityBehavior = UIGravityBehavior(items: [button]) //2
dynamicAnimator.addBehavior(gravityBehavior) //3
复制代码

详细情况如下:

  1. UIKitDynamicAnimator 我们创建了一个 UIKitDynamicAnimator 对象,它充当执行动画的协调器。我们还传递了作为引用视图的按钮的SuperView。

  2. UIGravityBehavior 我们创建了一个 UIGravityBehavior 对象,并将我们的按钮传递到注入此行为的数组元素中。

  3. addBehavior 我们给动画师添加了重力物体。

    这将创建如下所示的动画:

    注意按钮是如何从屏幕的中心(它的原始位置)掉到底部和后面的。

    我们应该告诉动画师考虑屏幕底部是地面。这里是 UICollisionBehavior 进入画面。

    var dynamicAnimator   : UIDynamicAnimator!
    var gravityBehavior   : UIGravityBehavior!
    var collisionBehavior : UICollisionBehavior!
    
    dynamicAnimator = UIDynamicAnimator(referenceView: self.view) //1
    
    gravityBehavior = UIGravityBehavior(items: [button]) //2
    dynamicAnimator.addBehavior(gravityBehavior) //3
    
    collisionBehavior = UICollisionBehavior(items: [button]) //4
    collisionBehavior.translatesReferenceBoundsIntoBoundary = true //5
    dynamicAnimator.addBehavior(collisionBehavior) //6
    复制代码
  4. UICollisionBehavior 我们创建了一个 UICollisionBehavior 对象并沿按钮传递,以便将行为添加到元素中。

  5. translatesReferenceBoundsIntoBoundary 启用此属性会告诉动画师将引用视图边界作为结束,在我们的示例中,这是屏幕的底部。

  6. addBehavior 我们在这里给动画师添加了碰撞行为。

    现在,我们的按钮应该按在地面上,静止不动,如下所示:

    挺不错的,不是吗?

现在,让我们尝试添加一个弹跳效应,使我们的对象感觉更真实。为此,我们将使用 UIDynamicItemBehavior 班级。

``
var dynamicAnimator   : UIDynamicAnimator!
var gravityBehavior   : UIGravityBehavior!
var collisionBehavior : UICollisionBehavior!
var bouncingBehavior  : UIDynamicItemBehavior!

dynamicAnimator = UIDynamicAnimator(referenceView: self.view) //1

gravityBehavior = UIGravityBehavior(items: [button]) //2
dynamicAnimator.addBehavior(gravityBehavior) //3

collisionBehavior = UICollisionBehavior(items: [button]) //4
collisionBehavior.translatesReferenceBoundsIntoBoundary = true //5
dynamicAnimator.addBehavior(collisionBehavior) //6

//Adding the bounce effect
bouncingBehavior = UIDynamicItemBehavior(items: [button]) //7
bouncingBehavior.elasticity = 0.75 //8
dynamicAnimator.addBehavior(bouncingBehavior) //9
```
复制代码
  1. UIDynamicItemBehavior 我们创建了一个 UIDynamicItemBehavior 对象并沿按钮传递,以便将行为添加到元素中。
  2. elasticity 数值必须在0-1之间,它代表弹性,即物体在地面上和地面上弹跳的次数。这就是魔术发生的地方--通过调整这个属性,你可以区分不同种类的物体,比如球、瓶子、硬物品等等。
  3. addBehavior 我们在这里给动画师添加了碰撞行为。

现在,我们的按钮在触地时应该会反弹,如下所示:

这个回购 是非常有用的,并显示了所有的UIKitDynamicsActions在行动中。它还提供了用于处理每一种行为的源代码。在我看来,这应该是一系列在视图上执行iOS动画的方法。

在下一节中,我们将简要介绍帮助我们测量动画性能的工具。我也建议你看看 优化Xcode构建的方法 因为它将节省大量的开发时间。

性能调谐

在本节中,我们将研究如何测量和调优iOS动画的性能。作为iOS开发人员,您可能已经使用Xcode工具(如内存泄漏和分配)来衡量整个应用程序的性能。同样,也有一些工具可以用来衡量动画的表现。

Core Animation 仪器

试试看 Core Animation 仪器和你应该能够看到你的应用屏幕提供的FPS。这是一个很好的方法来衡量任何动画呈现在你的iOS应用程序的性能/速度。

绘图

FPS在这个应用程序中被大大降低了,它显示的内容很重,就像图像中的阴影一样。在这种情况下,而不是直接将图像分配给 UIImageView 的图像属性,尝试使用CoreGraphicsAPI在上下文中分别绘制图像。当在单独的线程中而不是在主线程中执行图像解压缩逻辑时,这会过度地减少图像显示时间。

光栅化

Rasteralization是一个用于缓存复杂层信息的过程,以便这些视图在呈现时不会被重新绘制。重绘视图是FPS减少的主要原因,因此,最好对将要重复使用的视图应用栅格化。

包起来

最后,我还总结了 用于iOS动画的有用资源列表 。当你在iOS动画上工作时,你可能会发现这很方便。此外,您可能还会发现 这套设计工具 在深入研究动画之前,作为一个(设计)步骤很有帮助。

我希望我已经能够涵盖尽可能多的主题,围绕iOS动画。如果我在这篇文章中遗漏了什么,请在下面的评论部分告诉我,我很乐意做这个补充!

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章