Core Animation 之 View

最近切实的感觉到了本身在界面这一块基础原理的匮乏,所以决定深刻学习了解一下Core Animation, 可是在学习Core Animation的开端,不由又怀疑本身,UIWindow是什么?它的做用你真的明白吗?为何要有UIWindow呢,若是没有的话行不行呢?一系列问题层出不穷,所以我决定在研究学习Core Animation以前去学习学习苹果的官方文档View Programming Guide for iOS, 那么在开始的这一篇我只会去写一写我对于UIView的学习,总结,应该还会有一些问题,但愿和你们一块儿交流交流。html


可能在文中会用到不一样的词,可是他们是一个含义:
view == view对象 == 视图对象 == UIView对象
window == window对象 == 视窗对象 == UIWindow对象

(注:UIWindow的基类是UIView)
复制代码

View and Window

View究竟是什么?View对象在屏幕上定义了一块矩形的区域而且处理这个区域内部的事件。在UIKIT中,每个UIView对象都有一个CALayer对象的layer属性,layer对象负责视图内容的绘制,处理视图动画相关的事宜。视图由layer绘制,动画也由layer处理,那么UIView对象作什么呢?其实UIView对象能够理解为是对layer的封装,提供了处理触摸事件的具体功能以及Core Animation底层方法的接口。面试

什么是Core Animation呢?

image

Core Animation 是是iOS和OSX上可用的图形渲染和动画基础结构,用于为应用的视图和其余视觉元素设置动画。它把大量的绘图工做交给GPU去加速渲染,因此能够实现高帧率高流畅度的动画效果,并不会去负载CPU而使得手机卡顿。数组

Core Animation 并非一个绘图系统,而是使用硬件去合成和处理视图内容的基础框架,而这个基础框架的核心就是layer对象,那么layer又是如何对视图内容进行处理的呢?它会将其转化为GPU便宜处理的bitmap,而后GPU再去执行相应的操做,大多数APP中layer都是用来管理视图内容的,可是也能够去单独的建立独立的layer。bash

那么如今能够对Core Animation能够进行一些底层的了解了:markdown

Core Animation是一套包含图形绘制、投影、动画的Objective-C类集合,该框架包含在QuartzCore.framework中. app

image

上图的OpenGL ES是一个C语言写的很是底层的图形处理框架。是个移动设备上绘制2D和3D计算机图形的标准的开源库,普遍地被用在游戏的图形的绘制上负责直接驱动GPU,效率很高,可是使用起来很复杂。框架

Core Animation的核心就是对于OpenGL ES的抽象,它并不作渲染工做,而是使用OpenGL ES的功能,让GPU去处理渲染。从上图的结构能够知道Core Graphic 的绘制是须要消耗CPU的,而Core Animation渲染消耗的是GPU。Core Animation最繁重的任务是去判断出哪些layer须要被从新绘制,而OpenGL ES要作的就是将layer合并、显示在屏幕上。ide

工做的流程图

image

Core Graphic, Core Animation, Core Image是三个不一样的核心库,它们之间可能会有交互,可是不存在一个包含的关系。上图是它们的工做流程图oop

当你设置一个 layer 的内容为 CGImageRef 时,Core Animation 会建立一个 OpenGL 纹理,并确保在这个图层中的位图被上传到对应的纹理中。以及当你重写 -drawInContext 方法时,Core Animation 会请求分配一个纹理,同时确保 Core Graphics 会将你所作的(即你在drawInContext中绘制的东西)放入到纹理的位图数据中。一个layer的属性和 CALayer 的子类会影响到 OpenGL 的渲染结果,许多底层的 OpenGL ES 行为被简单易懂地封装到 CALayer 概念中。布局

回到主题 -> UIView

好了,走偏了咱们仍是继续聊一聊UIView吧。

视图的层次结构

每个父视图都会维持一个子视图数组,在数组最后一个子视图就是最上层的子视图,若是子视图重叠了,数组中靠后的子视图位于上方,父视图和子视图的关系会影响子视图的显示,在代码中常见的有两个场景

//1. 要求子视图不透明,父视图透明
layerView.backgroundColor = UIColor.black.withAlphaComponent(0.1)

//2. scrollView自动调整子视图size如何解决?
let scrollView = UIScrollView.init(frame: CGRect.init(x: 0, y: 0, width: 100, height: 100))
scrollView.autoresizesSubviews = false
复制代码
视图的响应事件

当谈及UIView和CALayer的主要区别时,最重要的区别就是UIView是能够响应事件的,那么View是如何响应事件的呢?简单来说分为两步:

  1. 寻找发生触摸事件的视图(从上往下)
  • 发生触摸事件以后,系统会将该事件加入到一个由UIApplocation管理的事件队列中
  • UIApplication会从事件队列中取出最靠前的事件向下分发,通常先给当前的KeyWindow
  • KeyWindow根据视图结构依次向下分发,直到找到最合适事件处理的view
  1. 找到事件的第一响应者(从下往上)
    事件响应
  • 事件响应由最底层的View开始,若是View处理不了,就向上找父View,一直找到当前Window,若是Window处理不了,会传给UIApplication处理,若是UIApplication处理不了就由UIApplecation的delegate处理。
视图的绘制周期

当view第一次在屏幕上显示的时候,系统会绘制它的内容,而后系统会截取内容的快照,而且将快照做为视图的可见外观,若是你永远不改变视图的内容,那么视图的绘制代码永远不会改变。若是更改了视图的内容,不用直接从新绘制,而是使用setNeedsDisplay或者setNeedsDisplayInRect方法是视图无效。这些方法会告知视图内容以及改变而且须要在下一次进行重绘。若是须要立马重绘,那么须要使用layoutIfNeeded方法。

  • 更改了Frame,或者Bounds的宽和高
  • 分配了包含缩放系数的transform属性
    contendMode
视图的伸缩

有时候只须要可视视图的一部份能够被伸缩,这个在设置按钮背景图片的场景是很是常见的,那么如何让这个常见的需求实现呢?使用resizableImageWithCapInsets:方法,能够肯定可缩放的视图的内容,以下:

var image = UIImage.init(named: "image.png")
image = image?.resizableImage(withCapInsets: UIEdgeInsetsMake(10, 10, 10, 10), resizingMode: UIImageResizingMode.stretch)
let imageview = UIImageView.init(image: image)
复制代码

伸缩背景的效果以下:(使四个角不变,中心进行缩放)

stretch

视图能够实现的动画

视图能够实现的动画效果的属性以下:

  • frame
  • bounds
  • center
  • transform
  • alpha
  • background
坐标形状和坐标系统

UIKit的坐标系以屏幕的左上角为原点

坐标
若是是OpenGL ES或者是Core Graphic的话是以屏幕的左下角为坐标原点的一个笛卡尔坐标系

视图须要注意的几点
  • 在修改视图的transform的时候,每一种变换都是针对视图中心而言的
  • 尽可能少的使用drawRact绘制
  • 只要有可能就显示声明视图为Opaque

回到主题之Window

那么window的职责是什么,为何要有Window的存在呢?没有Window行不行呢? 下面试官网的原文

* It contains your application’s visible content.
* It plays a key role in the delivery of touch events to your views and other application objects.
* It works with your application’s view controllers to facilitate orientation changes.
复制代码

若是把viewController拿出来看,咱们知道viewController通常对应着相应的应用内部模块,若是要和其余的应用交互呢?这个就须要靠Window了;固然还有一点是屏幕的方向,这个也是经过Window来控制的!

涉及窗口的任务

在大多数时候,咱们只须要在didFinishLaunchingWithOptions:中初始化window以后就不会去涉及其余的window操做了,若是使用IB的话,连window的初始化也没有必要了;可是在实际的代码过程当中仍然可能会涉及两个任务

  • 使用窗口对象将点和矩形转换为窗口的本地坐标系统或从窗口的本地坐标系统转换点和矩形
  • 经过窗口的通知来追踪窗口的变化
    • UIWindowDidBecomeVisibleNotification
    • UIWindowDidBecomeHiddenNotification
    • UIWindowDidBecomeKeyNotification
    • UIWindowDidResignKeyNotification
关于不一样的屏幕

其实吧,分屏是一个很是常见的功能,若是接触过度屏的话就须要去仔细的研究研究分屏相关的代码,这一块也是和UIWindow息息相关的。

回到主题之再谈View

关于视图的层次结构
  • 使用tag 使用viewWithTag:搜索对应的视图要比从父视图的子视图结构中搜索要快
  • 调整视图的层级结构(使用调整层级的方法比移除子视图而后从新插入更快) bringSubviewToFront: sendSubviewToBack: exchangeSubviewAtIndex:withSubviewAtIndex:
自动调整尺寸自动化调整布局变动

在布局中有一个很讨厌的事情,就是明明你的frame都已经设定好了,可是依然在运行的时候就会发生变化,其实不少这种状况的缘由就是被添加的子视图的frame会随着父视图的frame的变化而发生变化;其二就是有时候子视图须要保持和父视图一致的大小变换,或者维持原有的尺寸不变,这个时候就须要视图的自动化调整布局了。

父视图的 autoresizesSubviews 属性决定全部子视图是否须要调整。若是属性设置为 YES,视图会使用每一个子视图的 autoresizingMask 属性决定子视图的位置和尺寸。每一个子视图的尺寸变动会对它们的子视图触发一样的布局调整。每个子视图的autoresizingMask属性如何决定呢?

  • UIViewAutoresizingFlexibleHeight 当父视图高度变动时视图的高度也变动。若是这个常量没被包含,视图的高度将不会变动。
  • UIViewAutoresizingFlexibleWidth 当父视图宽度变动时视图的宽度也变动。若是这个常量没被包含,视图的宽度将不会变动。
  • UIVIewAutoresizingFlexibleLeftMargin 视图的左边缘和父视图的左边缘之间的距离根据须要增加或缩短。若是这个常量没有设置,视图的左边缘与父视图的左边缘之间的距离保持固定。
  • UIViewAutoresizingFlexibleRightMargin 视图的右边缘和父视图的右边缘之间的距离根据须要增加或缩短。若是这个常量没有设置,视图的右边缘与父视图的右边缘之间的距离保持固定。
  • UIViewAutoresizingFlexibleBottomMargin 视图的底部边缘和父视图的底部边缘之间的距离根据须要增加或缩短。若是这个常量没有设置,视图的底部边缘与父视图的底部边缘之间的距离保持固定。
  • UIViewAutoresizingFlexibleTopMargin 视图的顶部边缘和父视图的顶部边缘之间的距离根据须要增加或缩短。若是这个常量没有设置,视图的顶部边缘与父视图的顶部边缘之间的距离保持固定。
视图初始化

在使用code时: initWithFrame: 在使用nib时: initWithCoder: -> awakeFromNib

视图的绘制

在实际的操做中,要尽可能少的使用drawRect:方法,若是要使用此方法,明确它的职责:绘制内容。使用的时候要配置环境,绘制内容,尽量快结束绘制。 提升绘制性能的两个小tips:

  1. 若是你清楚你的视图绘制代码老是以不透明的内容覆盖视图的整个表面,你能够设置视图的 opaque 属性为 YES 提高系统性能。当你标记了视图为不透明时,UIKit 会避开在位于视图背后的绘制代码。这不是惟一缩减绘制花费的时间的方式但也最大限度减小视图和其余内容之间的合成工做。所以,若是你的视图的内容是彻底不透明的那么应该设置这个属性为 YES。若是你的视图不能保证内容老是不透明的,你应该设置这个属性为 NO。
  2. 另外一个提高绘制性能的方式,尤为在滚动过程当中,是设置视图的 clearsContextBeforeDrawing 属性为 NO。当这个属性设置为 YSE 时,在你的 drawRect: 方法调用以前 UIKit 把将要被 drawRect: 方法更新的区域自动填充为半透明的黑色。设置这个属性为 NO 会消除填充操做的开销但会加剧你的应用程序经过 drawRect: 方法填充更新矩形的内容的负担。
视图的动画

这个就很是常见了,大多使用的是UIView自带的Block动画,这个就不在细聊了,有一点想提一提,就是普通的动画只能是从A -> B两种状态之间进行切换,若是加上交互的话,动画就会变得不流畅了,因此Apple在iOS10添加了一个新的特性:UIViewPropertyAnimator ,这个在后面再聊吧。

总结

也不知道为何写了这么多,其实写的时候,主要仍是不少东西都是有关联的,一旦深刻其实还会有很是多的东西在里面,以上是个人学习和一些总结。若是写的很差,欢迎指出问题,这样我就能够慢慢改进了,下一篇,我会用比较短的篇幅聊一聊UIViewController,而后就正式的进入个人Core Animation的学习过程当中了。

相关文章
相关标签/搜索