使用UIKit和UIView在视图上执行iOS动画

使用UIKit和UIView在视图上执行iOS动画

时间:2020-7-20 作者:gykj

本文旨在成为iOS动画的入门,目的是详尽介绍各种不同的实现方式。

鉴于该主题的广泛性,我们将在相当高的层次上简要介绍每个部分。目的是教育读者一系列选择,以将动画添加到他/她的iOS应用程序中。

在开始与iOS相关的主题之前,让我们简要看一下动画速度。

以60FPS动画

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

FPS确定在一秒钟内翻转的静止图像的数量,这从字面上意味着图像/帧的数量越多,视频中显示的细节/信息越多。动画也是如此。

FPS通常用于确定动画的质量。普遍认为,任何好的动画都应以60fps或更高的速度运行-低于60fps的任何动画都会让人感觉有些不适。

您要查看30FPS和60FPS之间的区别吗?检查一下

您注意到差异了吗?人眼绝对可以感觉到较低fps的抖动。因此,始终要确保您创建的任何动画都遵循以60FPS或更高的速度运行的基本规则,这始终是一个好习惯。这使它感觉更加逼真和生动。

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

核心框架

在本节中,我们将介绍可用于创建视图动画的iOS SDK中的框架。我们将快速浏览它们中的每一个,并通过相关示例解释它们的功能集。

UIKIT / UIVIEW动画

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

提供给我们UIView的框架UIKit已经为我们提供了一些基本的动画功能,这些功能使开发人员可以轻松完成更多任务。

API UIView.animate是最简单的动画视图方法,因为通过以基于块的语法提供属性值,可以轻松地对任何视图的属性进行动画处理。

在UIKit动画中,建议仅修改UIVI的可设置动画的属性,否则会产生一些影响,在这些动画中,动画可能导致视图最终以意外状态结束。

动画(withDuration:动画:完成)

此方法需要动画时间,需要对一组视图的可动画属性更改进行动画处理。当完成执行动画的视图时,完成块将提供回调。

使用此单一API几乎可以实现视图上任何类型的动画,例如在视图上移动,缩放,旋转,淡化等。

现在,考虑要对按钮大小进行动画处理,或者要使特定视图放大到屏幕中。这是我们使用UIView.animateAPI的方法:

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
}

这是我们在这里做的事情:

  1. 我们UIView.animate使用传递给它的持续时间值调用该方法,该持续时间值表示该块内描述的动画应运行多长时间。
  2. 我们设置按钮的新框架,该框架应代表动画的最终状态。
  3. 我们将按钮设置center为其超级视图的中心,以使其保持在屏幕中心。

上面的动画代码块应触发按钮的帧从当前帧改变的动画:

Width = 0, Height = 0

到最后一帧:

Width = Height = newButtonWidth

这是动画的样子:

使用UIKit和UIView在视图上执行iOS动画
animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion

此方法类似于animate方法的扩展,在其中您可以执行在现有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)

这是我们使用的参数集:

  1. duration
    表示确定代码块应运行多长时间的动画持续时间。
  2. delay
    表示在动画开始之前我们想要的初始延迟。
  3. SpringWithDamping
    表示我们希望视图表现的弹性效果的值。该值必须在0到1之间。该值越小,弹簧振荡越高。
  4. velocity
    表示动画开始的速度。
  5. options
    要应用于视图动画的动画曲线的类型。
  6. 最后,是我们设置需要动画的按钮框架的代码块。与上一个动画相同。

这是上面的动画配置的动画外观:

使用UIKit和UIView在视图上执行iOS动画
UIViewPropertyAnimator

要对动画进行更多控制,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

这是我们正在做的:

  1. 我们UIViewProperty通过传递持续时间和动画曲线来调用API。
  2. 与上述两个UIView.animate API不同,除非您自行指定动画,否则动画将不会开始,即您完全控制了完整的动画过程/流程。

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

什么是A frame

从开始状态到最终状态的视图帧更改/过渡的集合定义为animation,动画期间视图的每个位置称为frame

动画关键帧

该API提供了一种设计动画的方式,您可以用不同的时间和过渡定义多个动画。发布此消息后,API会将所有动画简单地集成到一个无缝的体验中。

假设我们要以随机方式在屏幕上移动按钮。让我们看看如何使用关键帧动画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
    }
})

这是细分:

  1. duration
    通过传入动画的持续时间来调用API。
  2. delay
    动画的初始延迟时间。
  3. options
    要应用于视图动画的动画曲线的类型。
  4. animations
    包含开发人员/用户设计的所有关键帧动画的块。
  5. addKeyFrame
    调用API设计每个动画。在我们的例子中,我们定义了按钮的每个动作。我们可以根据需要将任意数量的动画添加到块中。
  6. relativeStartTime
    在动画块的集合中定义动画的开始时间。
  7. relativeDuration
    定义此特定动画的总体持续时间。
  8. center
    在我们的例子中,我们只需更改按钮的center属性即可在屏幕上移动按钮。

这就是最终动画的样子:

使用UIKit和UIView在视图上执行iOS动画

核心动画

任何基于UIKit的动画都会在内部转换为核心动画。因此,核心动画框架充当任何UIKit动画的支持层或主干。因此,所有UIKit动画API都不过是核心动画API的封装层(以易于消耗或方便的方式)。

UIKit动画API不能很好地控制已在视图上执行的动画,因为它们主要用于视图的可设置动画的属性。因此,在这种情况下,如果您打算控制动画的每一帧,最好直接使用底层的核心动画API。另外,UIView动画和核心动画也可以结合使用。

UIView +核心动画

让我们看看如何使用UIView和Core Animation 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按钮的属性设置为动画的最终值,否则动画完成后,按钮的cornerRadius值将自动恢复为其初始值。
  9. addAnimation
    通过表示需要对其执行动画的Keypath,将包含整个动画过程的配置的动画对象附加到该图层。
  10. commit
    表示动画代码块的结尾,并从动画开始。

这是最终动画的样子:

使用UIKit和UIView在视图上执行iOS动画

该博客非常有用,可以帮助您创建更高级的动画,它巧妙地引导您完成大多数Core Animation框架API的指导,并指导您逐步进行操作。

UIKITDYNAMICS

UIKit Dynamics是UIKit的物理引擎,使您可以向UIKit控件添加任何物理行为,例如碰撞,重力,推动,捕捉等。

UIKitDynamicAnimator

这是UIKit Dynamics框架的admin类,用于管理由任何给定的UI控件触发的所有动画。

UIKitDynamicBehavior

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

UIKitDynamics的不同类型的行为包括:

  • UIAttachmentBehavior
  • UICollisionBehavior
  • UIFieldBehavior
  • UIGravityBehavior
  • UIPushBehavior
  • UISnapBehavior

UIKitDynamics的体系结构看起来像这样。请注意,项目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对象,用作执行动画的协调器。我们还通过了按钮的超级视图作为参考视图。
  2. UIGravityBehavior
    我们已经创建了一个UIGravityBehavior对象,并将按钮传递到注入该行为的数组元素中。
  3. addBehavior
    我们已将重力对象添加到动画师。

    这将创建一个动画,如下所示:

    使用UIKit和UIView在视图上执行iOS动画
    请注意,按钮如何从屏幕的中心(其原始位置)掉到底部及其他位置。

    我们应该告诉动画师将屏幕的底部视为地面。这就是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
    我们在此处向动画师添加了碰撞行为。

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

    使用UIKit和UIView在视图上执行iOS动画

    那很整洁,不是吗?

    现在,让我们尝试添加一个弹跳效果,使我们的物体更真实。为此,我们将使用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
  7. UIDynamicItemBehavior
    我们已经创建了一个UIDynamicItemBehavior对象并传递按钮,以便将行为添加到元素中。
  8. elasticity
    值必须在0-1之间,它表示弹性,即物体在被击中时必须在地面上弹起或从地面弹起的次数。这就是魔术发生的地方-通过调整此属性,您可以区分不同种类的对象,例如球,瓶子,硬物等。
  9. addBehavior
    我们在此处向动画师添加了碰撞行为。

现在,当按钮撞到地面时,它应该会弹起,如下所示:

使用UIKit和UIView在视图上执行iOS动画

此存储库非常有用,它显示了所有正在运行的UIKitDynamics行为。它还提供了处理每种行为的源代码。我认为,这应该是在视图上执行iOS动画的方式的广泛清单!

在下一部分中,我们将简要介绍可帮助我们测量动画性能的工具。我还建议您考虑优化Xcode构建的方法,因为它可以节省大量的开发时间。

性能调优

在本节中,我们将探讨测量和调整iOS动画性能的方法。作为iOS开发人员,您可能已经使用Xcode工具(例如“内存泄漏”和“分配”)来衡量整个应用程序的性能。同样,有些工具可用于衡量动画的效果。

Core Animation 仪器

尝试使用该Core Animation仪器,您应该能够看到应用程序屏幕提供的FPS。这是一种衡量iOS应用中渲染的动画的性能/速度的好方法。

画画

应用程序中的FPS大大降低了,该应用程序显示大量内容(例如图像)和效果(例如阴影)。在这种情况下,请UIImageView尝试使用Core Graphics API在上下文中单独绘制图像,而不是直接将Image分配给的image属性。通过在单独的线程而不是主线程中异步执行图像解压缩逻辑,可以过度减少图像显示时间。

栅格化

栅格化是一种用于缓存复杂层信息的过程,以便这些视图在渲染时不会被重绘。重绘视图是降低FPS的主要原因,因此,最好对将要重复使用几次的视图应用栅格化。

包起来

最后,我还总结了一些有关iOS动画的有用资源。在处理iOS动画时,您可能会发现这非常方便。此外,在深入研究动画之前,这组设计工具还可以作为(设计)步骤有用。

我希望我能够涵盖有关iOS动画的尽可能多的主题。如果我可能在本文中错过了任何内容,请在下面的评论部分中告诉我,我很乐意添加!

版权所有:https://www.eraycloud.com 转载请注明出处