Core Animation 学习笔记1 - UIView & AL 动画

Core Animation 学习笔记1 - UIView & AL 动画

我自己的 Core Animation 的学习笔记,来自 Ray 家 iOS 最好的动画书籍 -- iOS Animations by Tutorials

支持正版


1.UIKit 动画 API

UIView 的最基本的动画方法如下:

open class func animate(withDuration duration: TimeInterval, animations: @escaping () -> Swift.Void)

带有延迟的方法

open class func animate(withDuration duration: TimeInterval, delay: TimeInterval, options: UIViewAnimationOptions = [], animations: @escaping () -> Swift.Void, completion: (@escaping (Bool) -> Swift.Void)? = nil)

options 用来配置动画的一些参数,具体后面有详细。

可以传入数组,数据的元素是 UIViewAnimationOptions 枚举的元素,代表选择多个动画的配置参数,传入[] 代表为空。

1.1 动画属性

UIView 的动画 API 中,不是所有的属性在修改的时候会形成动画效果的。

常见的可以产生动画的属性

bounds, frame, center

基本的定位和 view 的大小 Size 是可以被修改的,这其中包括了 view 的 bounds, frame 和 center,通过修改这三个属性来实现 放大 缩小移动

backgroundColor 和 alpha

· 外观相关的属性有 UIView 的 背景颜色 backgroundColor 和 透明度 alpha 值。

Transformation 视图变形 / 仿射变换

· 在动画的 block 中改变 UIView 的 偏转角度(rotation),大小缩放(scala),定位(position)

Transformation 跟 第一种的区别在于:

  1. Transformation 的底层实现是由 Affine Transform 仿射变换的原理完成,具体原理是通过一系列的矩阵数学计算来完成,(具体原理稍后研究补上);而 UIView 属性就是通过改变 UIView 的frame center等属性完成的动画。

  2. 从某些变化的角度来说,Transform 更强大,比如它可以控制旋转角度,可以仅仅提供一个 scala 参数而无需提供具体的frame,某些参数更加贴近现实语言。

同样是让视图变化,Transformation 是由 Affine Transform 仿射变换的原理完成,不会强硬的去改变


1.2 基本的可选配置

动画方法的 options 中通过一个数组来配置动画的一些属性。

options 传参的四种写法:

1. []                         代表无参数
2. .Repeat                    代表一个 .Repeat 参数
3. [.Repeat]                  代表一个 .Repeat 参数
4. [.Repeat, . Autoreverse]   代表两个参数

下面列举了可选的参数:

重复

.Repeat :会让你的动画重复循环

与之对应的有一个参数是

.Autoreverse :配合着 .Repeat 一起用,代表自动反转,如下例子:

UIView.animateWithDuration(0.5, delay: 0.4, options: [.Repeat, .Autoreverse], animations: {
    self.password.center.x += self.view.bounds.width
    }, completion: nil)

慢入慢出

.EaseIn, .EastOut 的设置让你的元素动画起来更符合现代物理学的特征,引入了加速度和减速度的概念,显得更加真实,不会突然启动,也不会突然停止。

option 中通过四个可选参数来控制:

.Linear :代表匀速,没有加速和减速的存在。

.CurveEasyIn : 在动画开始的时候加速度启动

.CurveEasyOut : 在动画开始的时候减速度结束

.CurveEasyInOut : 结合上面两中,开始和结束的时候有加速度和减速度。慢入慢出的效果。


2. 弹性动画

    open class func animate(withDuration duration: TimeInterval, delay: TimeInterval, usingSpringWithDamping dampingRatio: CGFloat, initialSpringVelocity velocity: CGFloat, options: UIViewAnimationOptions = [], animations: @escaping () -> Swift.Void, completion: (@escaping (Bool) -> Swift.Void)? = nil)

关键词是 usingSpringWithDamping,这里的两个属性:

Damping,这个代表着阻尼,取值范围从 0.0~1.0;

Velocity,代表元素的起始速度,初速度越大,弹的幅度也就越大

3. Transitions 过渡动画

过渡动画是提前预定义好的一些列动画,你可以直接使用,使得界面过渡自然。有很多 3D 效果,大部分我们在 iPhone 历代中都见过,可以写 Demo 都试一遍,非常方便使用。

各种使用场景:

3.1 添加新 View:

public class func transitionWithView(view: UIView, duration: NSTimeInterval, options: UIViewAnimationOptions, animations: (() -> Void)?, completion: ((Bool) -> Void)?)

在动画 closure 内简单的 addSubview 即可

这个方法跟之前的 UIView 动画方法类似,但是多了一个 view 的参数,这个参数穿进去的 view 可以被 options 中的预定义动画参数定义过渡动画。

.transitionFlipFromLeft
.transitionFlipFromRight
.transitionCurlUp
.transitionCurlDown
.transitionCrossDissolve
.transitionFlipFromTop
.transitionFlipFromBottom

view: 的参数写要添加的View的superView。

3.2 移除 View:

跟上面的方法一样,在动画 closure 内简单的调用 removeFromSuperview 即可完成,view 参数传要移除的 view 的 superView。

3.3 隐藏显示:

跟上面的方式一样,view参数传需要改变 alpha 值的 view 本身。

3.4 替换图片

替换的过渡效果的方法是:

 public class func transition(with view: UIView, duration: TimeInterval, options: UIViewAnimationOptions = [], animations: (() -> Swift.Void)?, completion: ((Bool) -> Swift.Void)? = nil)

from 与 to 的都是 View。

4. 链式动画 关键帧动画

keyFrames 动画核心是下面两个 API,UIView 调用一个animateKeyframes 方法,然后在这个方法的 closure 里面去 调用 UIView.addKeyframe 方法来添加关键帧动画。

animateKeyframes 动画的 options 是 'UIViewKeyFrameAnimationOptions',跟前面的不一样,之前的动画是 UIViewAnimationOptions

extension UIView {

    @available(iOS 7.0, *)
    public class func animateKeyframes(withDuration duration: TimeInterval, delay: TimeInterval, options: UIViewKeyframeAnimationOptions = [], animations: () -> Swift.Void, completion: ((Bool) -> Swift.Void)? = nil)

    @available(iOS 7.0, *)
    public class func addKeyframe(withRelativeStartTime frameStartTime: Double, relativeDuration frameDuration: Double, animations: () -> Swift.Void) 
    
    // start time and duration are values between 0.0 and 1.0 specifying time and duration relative to the overall time of the keyframe animation
}

UIView.addKeyframe 添加关键帧动画的方法有好多个,上面这个方法涉及两个参数:relativeStartTimerelativeDuration

  1. relativeStartTime : 指这个动画在整个关键帧动画中的开始时间,是一个相对时间,比如这个参数设置为 0.2,就是在整个动画的 duration * 0.2 开始,理解为 20% 的时候开始这个子动画

  2. relativeDuration : 动画的持续时间,也是一个相对值,0.25就是执行时间占整个动画的 25%

最后,关键帧动画本身不支持内建的慢入慢出动画,关键帧动画本身是设计来解决固定开始结束时间的动画 和 在动画序列之间顺序切换的动画,所以在 EasyInOut 上不做内建支持,也就是说它没办法像 UIView 标准动画一样通过设置一个 EasyInOut 的 options 参数就实现令人满意的慢入慢出效果。

替代方案是有一系列计算模型,官方文档中 UIViewKeyframeAnimationOptions 有详细描述。

CG放射变幻的角度:
self.planeImage.transform = CGAffineTransform(rotationAngle: (CGFloat(-M_PI_4/2)))

5. Auto Layout Animation

获取已有的constant

可以通过 IB 工具把 constant 设置为一个 outlet,也可以通过 reference 在代码创建的时候设置一个 weak 引用。

运行时可以通过循环遍历找到 constant,遍历用 swift 的数组属性遍历方法,来找到正确的那一个 constraint:

view.superview?.constraints.forEach { constraint in 
    //your code...
    if constraint.firstItem == titleLabel && 
      constraint.firstAttribute == .centerX {
      constraint.constant = isMenuOpen ? -100.0 : 0.0
      break
    }
}

更快遍历找到一个 constraint,可以在 IB 中设置这个 constraint 的 Identifier。然后 for-in 遍历的时候直接判断 ID :

if constraint.identifier == "TitleCenterY" 

修改 constant 产生动画

在动画闭包之前手动更新 Layout,然后在动画 Closure 中调用 layoutIfNeeded

一般使用 AL 来做静态的布局,使用了 AL 之后就无法重新设置 UI 元素的 center bounds 以及 frame 了,否则会根据 constraints 走,再做动画的时候就必须做 AL 相关的动画。

AL 动画跟 UIView 属性的动画都很相似,只不过 AL 动画一般是替换了之前的约束,然后让 UIView 元素的两个状态动画变换起来。

实现起来就是 Outter 需要变更的 Constraint,然后改变 约束的 constant。然后在改变 constant 的代码后面加上

    UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 0.4, initialSpringVelocity: 10.0, options: .curveEaseIn, animations: {
        self.view.layoutIfNeeded()
    }, completion: nil)

在 animation 的 closure 中,调用 layoutIfNeeded,强制更新布局,产生动画。

替换 constraint 产生动画

通过设置一个 constant 的 isActive 属性为 false,可以暂时关闭这个 constant,然后通过代码正常添加一个 constant 或者激活一个新的,产生替换 constant 的效果。

创建一个新的 constant 并设置 isActive 为 true:

let newConstraint = NSLayoutConstraint(
  item: titleLabel,
  attribute: .centerY,
  relatedBy: .equal,
  toItem: titleLabel.superview!,
  attribute: .centerY,
  multiplier: isMenuOpen ? 0.67 : 1.0,
  constant: 5.0)
newConstraint.identifier = "TitleCenterY"
newConstraint.isActive = true

新View动画

首先像正常一样创建一个 View,加到 subview 里,然后添加 constraints。

可以使用 NSLayoutAnchor 这个新类,让你创建 constraint 的过程变得简单,常用的一些都很好用,下面代码最后几行每一行都是创建一个 NSLayoutAnchor :

    func showItem(_ index: Int) {
        print("tapped item \(index)")
        
        let imageView = UIImageView(image: UIImage(named: "summericons_100px_0\(index).png"))
        imageView.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.5)
        imageView.layer.cornerRadius = 5.0
        imageView.layer.masksToBounds = true
        imageView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(imageView)
        
        let conX = imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        let conBottom = imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: imageView.frame.height)
        let conWidth = imageView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.33, constant: -50.0)
        let conHeight = imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor)
        NSLayoutConstraint.activate([conX, conBottom, conWidth, conHeight])
    }

⚠️如果是代码使用的 AL,需要设置 translatesAutoresizingMaskIntoConstraints 为 false。

创建好了 constraints 之后,确保这些 constraints 标明了动画的起始位置,然后调用一遍 layoutIfNeeded,进行静态的布局。

然后像上面一样,调用 UIView 的标准动画方法,弹性或者慢入慢出都可以,closure 中然后修改正确的 constant 值,调用 layoutIfNeeded 方法就生效了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容

  • 概览 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你...
    被吹落的风阅读 1,540评论 1 2
  • iOS 平台下酷炫而流畅的动画效果着实值得标榜,恰当的时机加入动画效果可以极大的提升用户体验。iOS 平台的动画,...
    CyrusCao阅读 1,581评论 0 50
  • 包含 CABasicAnimation 的一些简单动画,Spring 后面的再学。 最近开始忙项目了,停较长一段时...
    扬仔360阅读 479评论 0 1
  • 在iOS实际开发中常用的动画无非是以下四种:UIView动画,核心动画,帧动画,自定义转场动画。 1.UIView...
    请叫我周小帅阅读 3,053评论 1 23
  • 1. 这两天,看微博的言论是这样的 知乎的言论是这样的。 两边的用户互相看不起对方,好像上知乎的就不看微博,用微博...
    拾久阅读 448评论 0 2