用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的框架,它已经为我们提供了一些基本的动画功能,使得开发人员可以通过更少的操作来实现更多的目标。
apiuiview.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
表示我们希望视图表现的弹性效果的值。数值必须在0到1之间。值越低,弹簧振荡越高。 -
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通过传递持续时间和动画曲线。 - 与上面的uiview.动画api不同,除非您自己指定动画,即完全控制完整的动画过程/流,否则动画不会启动。
现在,让我们假设你想要更多的控制动画。例如,您希望设计和控制动画中的每个帧。还有另一个apianimatekeyframes
。但是在我们深入研究它之前,让我们快速地看看一个框架是什么,在一个动画中。
什么是aframe
?
视图的框架更改/转换集合(从开始状态到最终状态)定义为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
通过传递动画的持续时间来调用api。 -
delay
动画的初始延迟持续时间。 -
options
要应用于视图动画的动画曲线类型。 -
animations
块,该块接受开发人员/用户设计的所有关键帧动画。 -
addkeyframe
调用api来设计每个动画。在我们的例子中,我们定义了按钮的每一个动作。我们可以有更多的这样的动画,我们需要,添加到块。 -
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
详细情况如下:
-
begin
表示动画代码块的开始。 -
duration
整体动画持续时间。 -
curve
表示需要应用于动画的计时曲线。 -
uiview.animate
我们的第一个动画改变框架的按钮。 -
cabasicanimation
我们创建cabasicanimation
引用cornerradius
按钮作为键盘,因为这是我们想要的动画。类似地,如果希望对关键帧动画具有粒度级控制,则可以使用cakeyframeanimation
班级。 -
fromvalue
表示动画的起始值,即cornerradius
从动画必须从哪里开始的按钮的值。 -
tovalue
表示动画的最终值,即最终值。cornerradius
动画必须结束的按钮的值。 -
cornerradius
我们必须设置`cornerradius属性的属性,以动画的最终值,否则,按钮的角半径值将在动画完成后自动恢复到其初始值。 -
addanimation
通过表示动画需要执行的keypath,我们将包含整个动画过程配置的动画对象附加到该层。 -
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
详细情况如下:
-
uikitdynamicanimator
我们创建了一个uikitdynamicanimator
对象,它充当执行动画的协调器。我们还传递了作为引用视图的按钮的superview。 -
uigravitybehavior
我们创建了一个uigravitybehavior
对象,并将我们的按钮传递到注入此行为的数组元素中。 -
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 ```
-
uicollisionbehavior
我们创建了一个uicollisionbehavior
对象并沿按钮传递,以便将行为添加到元素中。 -
translatesreferenceboundsintoboundary
启用此属性会告诉动画师将引用视图边界作为结束,在我们的示例中,这是屏幕的底部。 -
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 ```
-
uidynamicitembehavior
我们创建了一个uidynamicitembehavior
对象并沿按钮传递,以便将行为添加到元素中。 -
elasticity
数值必须在0-1之间,它代表弹性,即物体在地面上和地面上弹跳的次数。这就是魔术发生的地方--通过调整这个属性,你可以区分不同种类的物体,比如球、瓶子、硬物品等等。 -
addbehavior
我们在这里给动画师添加了碰撞行为。
现在,我们的按钮在触地时应该会反弹,如下所示:
这个回购是非常有用的,并显示了所有的uikitdynamicsactions在行动中。它还提供了用于处理每一种行为的源代码。在我看来,这应该是一系列在视图上执行ios动画的方法。
在下一节中,我们将简要介绍帮助我们测量动画性能的工具。我也建议你看看优化xcode构建的方法因为它将节省大量的开发时间。
性能调谐
在本节中,我们将研究如何测量和调优ios动画的性能。作为ios开发人员,您可能已经使用xcode工具(如内存泄漏和分配)来衡量整个应用程序的性能。同样,也有一些工具可以用来衡量动画的表现。
core animation
仪器
试试看core animation
仪器和你应该能够看到你的应用屏幕提供的fps。这是一个很好的方法来衡量任何动画呈现在你的ios应用程序的性能/速度。
绘图
fps在这个应用程序中被大大降低了,它显示的内容很重,就像图像中的阴影一样。在这种情况下,而不是直接将图像分配给uiimageview
的图像属性,尝试使用coregraphicsapi在上下文中分别绘制图像。当在单独的线程中而不是在主线程中执行图像解压缩逻辑时,这会过度地减少图像显示时间。
光栅化
rasteralization是一个用于缓存复杂层信息的过程,以便这些视图在呈现时不会被重新绘制。重绘视图是fps减少的主要原因,因此,最好对将要重复使用的视图应用栅格化。
包起来
最后,我还总结了用于ios动画的有用资源列表。当你在ios动画上工作时,你可能会发现这很方便。此外,您可能还会发现这套设计工具在深入研究动画之前,作为一个(设计)步骤很有帮助。
我希望我已经能够涵盖尽可能多的主题,围绕ios动画。如果我在这篇文章中遗漏了什么,请在下面的评论部分告诉我,我很乐意做这个补充!
作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的ios交流群:1012951431 不管你是小白还是大牛欢迎入驻 ,分享bat,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!
另附上一份各好友收集的大厂面试题,进群可自行下载!
下一篇: 浏览器跨域问题分析