iOS的UI基础

Core Animation

iOS系统的核心地位:应用内和应用间都会应用(多个程序间的手势切换)SpringBoardBackBoard。

应用内的工作:Core Animation Pipeline管线(layout,display,preload,commit)。

应用外的工作:提交到Render Server服务,反序列化构建渲染树GPU的工作:提交到GPU进行合成和渲染,输出到缓冲区。

注1:代码只能控制CPU处理中的管线中的布局和显示两个阶段(通过setNeedLayout和setNeedDisplay)(setNeedDisplayInRect还能设置脏矩形来减少不必要的绘制)。

注2:CPU偏计算;而图像的处理和渲染,用硬件会更快,因为GPU对图像计算进行了优化。


隐式动画

概念:更改一个CALayer的可做动画属性,Core Animation会执行一个隐式动画,平滑过渡到新值。动画的执行时间取决于事务的设置,动画的类型取决于图层行为。

实现:layer属性更改时会调用actionForKey方法,然后执行actionForLayer:forKey:代理方法(UIView禁用隐式动画的原理),如果没有实现代理,就查找属性-名称的actions映射字典和style字典,最后直接调defaultActionForKey。查找完actionForKey方法会返回一个CAAction做动画或NULL不做动画。

时机:由事务的管理机制控制。

注:UIView更改默认没有隐式动画,因为UIKit禁用了:UIView实现了CALayerDelegate的-actionForLayer:forKey方法,当属性的更改不在一个动画块中时,所有图层行为都会返回null,以此来禁用隐式动画。


显式动画

CABasicAnimation和CAKeyframeAnimation

虚拟属性:keyPath = "transform.scale .position .rotation",不用创建CATransform3D指定动画变换。

[layer removeAnimationForKey];  // 通过key在动画过程中取消动画。

动画的暂停

CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];

layer.speed = 0.0;

layer.timeOffset = pausedTime;

动画的继续

CFTimeInterval pausedTime = [layer timeOffset];

layer.speed = 1.0;

layer.timeOffset = 0.0;

layer.beginTime = 0.0;

CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;

layer.beginTime = timeSincePause;


事务

事务是一系列属性动画集合,通过CATransaction管理事务组。CATransaction没有属性和实例方法,只能通过+begin和+commit管理出入栈,+setAnimationDuration管理事务的动画时长和+animationDuration获取值(默认0.25),setCompletionBlock:可以设置事务完成后的回调。

事务的管理机制

每个RunLoop周期都会自动开始一个新的事务,把更改的属性收集起来做一次默认0.25s的隐式动画。

注:一般手动更改隐式动画的时间,需要新开事务避免影响别的动画(如屏幕旋转)。

UIView的动画

UIView的begin/commitAnimation底层原理也是设置了CATransaction。

UIView的animateWithDuration:animations:的Block动画方法其实也是内部自动调用了CATransaction的begin和commit,这样Block中的属性改变都会被事务所包含。


呈现树

presentationLayer其实更改图层属性后,属性值立刻更新,但屏幕没有马上发生改变,因为它只是定义了图层动画结束后将要变化的外观,即图层树模型。但在设置新值后和新值生效前,屏幕渲染的过程中,当前显示的属性值,在某些场景下更有意义:1、在基于定时器的动画过程中获取当前图层的准确位置;2、在做动画的图层中,通过hitTest响应用户交互。(touchesBegan/Target-Action中,判断触摸点是否在presentationLayer内)

注:关键方法:locationInView / convertPoint->Point转化触摸点坐标系,presentationLayer -> CALayer获取呈现树中的视图位置,hitTest -> CALayer/ Contains-> Bool返回触摸的Layer / 判断触摸点是否在Layer上。


图片IO

imageWithContentsOfFile方法加载本地目录的图片文件,会在加载到内存之后才进行解码解压,所以会在绘制的时候影响性能,且不会进行内存缓存,适用于读取一次性大图的场景imageNamed可以避免延迟加载,会马上解压并加载到内存中缓存起来,但是只对assets中的资源有效。

强制解压和提前渲染

使用CGContext从上下文中绘制一个新的图片(直接绘制成CGImageRef位图)来代替(或绘制成一个点也会解压但不会节省绘制时间),优化了因解压增加的绘制时间,还可以丢弃解压后的图片节省内存,但需要更复杂的计算,CPU消耗总量增加。

总结:使用CGContext异步重绘图片,得到解压缩后的位图。


UIView和CALayer

View持有Layer用于显示,View可以响应交互负责管理,View的属性大部分从Layer映射而来,UIView支持自动布局;Layer的delegate是View,动画会通知View,Layer更轻量级。

提供View和layer两个平行的层级关系是为了职责分离,避免重复代码。


frame和bounds

frame:外部坐标,在父图层占据的位置bounds:内部坐标本地坐标系,作用于子视图,左上角通常是{0, 0}(通过更改bounds实现简单的scrollView)

注:frame是一个虚拟属性,是根据bounds+position+transform计算而来;当对图层进行旋转或缩放时,frame实际是变换后,补齐的正矩形区域,即宽高可能和bounds不一致。


anchorPoint

锚点,UIView未暴露的属性,默认中心{0.5, 0.5},左上角{0, 0} 右下角{1, 1},小于0大于1将放置在图层外。UIView的Center和CALayer的Position属性加上锚点,才是图层的实际位置,所以图层锚点更改,frame会发生变化。


masksToBounds

Layer属性,设置为YES截取子图层实现圆角(cornerRadius);设为NO避免截取阴影(shadowOpacity,shadowColor,shadowOffset,shadowRadius,shadowPath-CGPath)。


mask蒙版

Layer属性,定义了父视图的可见区域,mask图层的颜色不重要,实心部分将会保留,其他隐藏


CGAffineTransform

UIView 仿射变换,仅支持2D变换:MakeRotation,Scale,Translation等。

CATransform3D

CALayer 3D变换,方法和仿射变换类似,加入zPosition形成4*4矩阵。

CAShapeLayer

基于CGPath路径创建CAShapeLayer矢量图形(非CG的Bitmap),bezierPathWithRoundedRect: byRoundingCorners: cornerRadii:可以单独指定每个角的圆角,作为蒙版为宿主层加圆角(mask)。

AVPlayerLayer

AVFoundation提供,和CA紧密结合,是CALayer的子类。AVPlayer + AVPlayerLayer简单的播放视频。


UIView的布局更新和重绘机制

-loadViewIfNeeded

VC初始化后,强制提前执行viewdidload加载控件和数据装载。

-layoutSubviews

此函数用于,父视图的布局、大小等发生了改变,你想要同时更新其子视图的布局时,更新子视图布局的代码应写在父类重写后的layoutSubviews里(它默认不做任何事),但这时就不能在外部设置子视图的布局了。

注:初始化含frame(且显示出来)或改变frame,addSubview,滚动UIScrollView,旋转屏幕,改变子view的大小会触发layoutSubviews。

-setNeedsLayout

设置布局刷新标记——标记为需要重新布局。此方法会异步触发layoutSubviews(执行完成后刷新标记又置为NO),如果想立刻触发,应立刻调用layoutIfNeeded。

-layoutIfNeeded(写在animation内,用于实现更新约束时的动画效果)

view.layoutIfNeeded()后frame及坐标为约束完成后的值,可用于viewdidload中获取准确的frame。

配合setNeedsLayout,在有刷新标记的情况下立刻触发layoutSubviews。但在view的init方法后、第一次显示前(addSubview前),标记是“需要刷新”的。

self.testView= [[LayoutTestViewalloc]initWithFrame:CGRectMake(0,0,300,300)];  //初始化会调用两次setNeedsDisplay

//此时视图标记是“需要刷新”的,直接调用[self.testView layoutIfNeeded];会触发layoutSubviews,但之后的addSubview就不触发了

[self.view addSubview:self.testView]; //默认触发layoutSubviews

self.testView.frame = CGRectMake(0,0,500,500); //改变frame会再次触发layoutSubviews()

- (void)click {

      [self.testView setNeedsLayout]; //标记为可刷新且异步触发layoutSubviews

      [self.testView layoutIfNeeded]; //立刻触发layoutSubviews

      //只触发一次

}

Drawing and Updating the View

-drawRect:(CGRect)rect(内存杀手)

view首次显示或调用setNeedsDisplay时执行。

重写此方法,执行自定义绘制内容任务。默认不起作用。永远不要直接调用drawRect。重写此方法多用于UIView结合CGContextRef的手动绘图(与CALayer对应的是drawInContext)

CGContextRef ctx =UIGraphicsGetCurrentContext(); //获取当前画板(图形上下文,只在UIView的drawRect中有效)(还可以使用)

注:view重写drawRect后如果不指定背景色或把opaque设成NO背景将变黑。

实际需求:1、在当前画板上画图像,曲线、虚线、几何图形、写字等等。

2、UITextView添加自定义placeholder的需求中,将holder的label添加在写drawRect内,设置holder字符串时调用setNeedsDisplay重绘UITextView。

-setNeedDisplay

在下一个绘画周期(1/60秒后)主动触发drawRect执行重绘。setNeedDisplayInRect方法可限定在rect范围内重绘。一般结合重写drawRect来使用。

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

推荐阅读更多精彩内容