iOS课程观看笔记(一)---UI视图相关

简介:

于海老师的《资深大牛带你深度剖析ios面试》算是看完了,于海老师思路清晰、语言流畅、把握要点,把很多难点问题可以很好的讲通,还有很多偏角旮旯自己不知道或没有掌握的知识也是从这上面学习到的,看的人有种拍案叫绝的感觉。整体来看,这套课程是难得的精品课程。
当然,也有不足之处,所讲知识都是直戳要害,没有进行发散,对新手不是很友好,不过也是,这门课本身就是面向中高级iOS程序员的。
有些地方自己知道的,可以加深了解,串联知识点。
有些地方自己并没听说过,自己想弄明白需要下功夫查些资料。
看完,不等于学完,不做笔记,俩月、半年后不看,还是会忘记。
短短15小时课程,做笔记的话,每一节课的知识量都是很多,因此,自己花时间对视频课内容进行整理,与君共勉。

iOS课程观看笔记(一)---UI视图相关_第1张图片
系统的UI传递机制是怎样的?
KVO的实现原理是怎样的?
简单说说消息传递机制和消息转发流程
当一个obj废弃的时候,指向它的weak指针为何会自动置位nil?
iOS如何进行内存管理的?
Block的实质是怎样的?使用Block为何容易产生循环引用?
简单说说怎样利用GCD实现高效的多读单写逻辑?
RunLoop为何能做到有事做事,没事休息?
怎样解决DNS劫持?
分别说说什么是桥接模式、责任链模式?
怎样设计一个图片缓存框架?
怎样设计一个网络框架?AFNetworing
请编写一个算法,查找一个字符串中,第一个只出现一次的字符。
AFNetworing大致是怎样实现的?


UI视图部分

iOS课程观看笔记(一)---UI视图相关_第2张图片

1.UITableView

1.1 重用机制

iOS课程观看笔记(一)---UI视图相关_第3张图片

屏幕向上滑动,A1消失,A7即将出现,则A7可利用A1所在的内存地址,从而达到重用机制。

1.2 数据源同步

iOS课程观看笔记(一)---UI视图相关_第4张图片

在一个tableView列表中,在主线程进行了删除操作,而在子线程进行了加载更多操作。如此一来,可能会造成数据源对照不上问题。

问题是,我觉得loadMore并不会造成数据源问题。
比如原有数据10条,存储在一个SourceMuttableArray里面
删除第2条数据,是对原有数据进行操作。
loadMore是加载更多数据,假入加载了新的10条数据,加载完毕后会放入SourceMuttableArray里面。
虽然是对同一个数组进行操作,但是老数据与新数据并没有相互影响。

此处如若改成reloadData刷新数据比较恰当。
如果是刷新操作,老数据删除,然而新的请求并不知道,刷新后被删除的数据又出现了,从而出现问题。

解决方案一:并发访问、数据拷贝

iOS课程观看笔记(一)---UI视图相关_第5张图片

在主线程删除操作的时候,进行记录删除操作,然后在子线程数据返回的时候,再同步删除操作。从而达到数据同步。
这种,相当于手动将从服务器数据加载到本地的数据进行了修改。

大致相当于这种操作:

-void)DeleteData
{
     
	BOOL isHaveDeleteData = YES;
	int c = indexPath.row;
}

- (void)reloadData
{
     
	//数据请求下来了,存储在临时数组tempDataArray
	if(isHaveDeleteData){
     
		[self.tempDataArray removeObjectAtIndex:tempDataArray];
		self.sourceDataArray = [NSMuttableArray arrayWithArray:self.tempDataArray];
	}
}

解决方案二:串行访问
iOS课程观看笔记(一)---UI视图相关_第6张图片

利用GCD,创建串行队列,将子线程的返回的数据与主线程的删除数据操作,都放在串行队列中。


UIView和CALayer

iOS课程观看笔记(一)---UI视图相关_第7张图片

UIView里面的layer属性,其实就是CALayer类型。
CALayer里面有一个contents,负责显示
contents里面有一个backing store是一个bitmap位图,最终进行显示。

UIView为CALayer提供显示的内容,以及负责处理触摸等事件,参与响应链
CALayer通过contents负责显示内容

该机制体现了设计原则中的:单一设计原则

事件传递与视图响应链

问:当用户点击了C2位置,是怎样一个事件传递过程以及响应过程?

iOS课程观看笔记(一)---UI视图相关_第8张图片
事件传递,有两个关键的方法:

//作用:判断哪一个视图响应,就返回哪一个UIView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
     
    1.若当前视图无法响应事件,则返回nil
    2.若当前视图可以响应事件,同时有子视图可以响应,则返回子视图层次中的事件响应者
	3.若当前视图可以响应事件,但无子视图可以响应事件,则返回自身作为当前视图层次中的事件响应者
}

//作用:判断触摸点是否在当前View上
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
     
	
}

事件传递流程图
iOS课程观看笔记(一)---UI视图相关_第9张图片

– (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event的内部实现图
iOS课程观看笔记(一)---UI视图相关_第10张图片
用代码表现出来大致是:
iOS课程观看笔记(一)---UI视图相关_第11张图片

问:如何将一个方形的Button,只有圆形区域可以响应点击事件,而圆形外部的区域无法响应点击事件?

iOS课程观看笔记(一)---UI视图相关_第12张图片
在UIButton中,重写-pointInside:withEvent:方法
iOS课程观看笔记(一)---UI视图相关_第13张图片

老师程序中还重写了- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event方法,感觉没必要,只重新pointInside即可满足

在这个基础上,被问过扩展型的问题:

有一个UIView(A),上面有一个UIButton(b),UIButton的大小和UIView的大小一样,现在,在UIButton中重写pointInside:withEvent:,将其点击区域扩大2倍,问,该方法能否实现想要的效果?

答:不可以

点击后,点击从上往下找合适的View,当点击是在UIView(A)上,最后由UIButton接收事件,响应。
当点击点是在UIButton的外部,其实也是UIView(A)的外部,此时UIView(A)里面的pointInside:withEvent:返回NO,即,触摸点没有在UIView(A)上面,也就不会UIView(A)里面的子类了,也就是不会走UIButton的重新后的pointInside:withEvent:。

也就是,触摸点不在父类上,则不会执行到子类。因此,重新子类pointInside:withEvent:方法也没有用。


2020-11-30更新

孟浪了,以上答案错误
正确答案是: 可以实现将UIButton扩大点击区域

做了两个实验:
首先,UIView大小为(100, 100, 100, 100),UIButton加到UIView上,大小为(0, 0, 50, 50),通过重写UIButton的-(BOOL)pointInside:withEvent:

- (void)viewDidLoad {
     
    [super viewDidLoad];
    
    UIView *redView = [[UIView alloc] init];
    redView.frame = CGRectMake(100, 100, 100, 100);
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];
    
    YZButton *btn = [[YZButton alloc] init];
    btn.frame = CGRectMake(0, 0, 50, 50);
    btn.backgroundColor = [UIColor yellowColor];
    [btn addTarget:self action:@selector(btnClick) forControlEvents:UIControlEventTouchUpInside];
    [redView addSubview:btn];
}

#import "YZButton.h"
@implementation YZButton
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
     
    //获取自己的bounds
    CGRect bounds = self.bounds;
    //扩大原热区直径至100,可以暴露个接口,用来设置需要扩大的半径。
    CGFloat widthDelta = MAX(100, 0);
    CGFloat heightDelta = MAX(100, 0);
    //CGRectInset,负数是向外,正数是向里
    bounds = CGRectInset(bounds, -0.5 * widthDelta, -0.5 * heightDelta);
    //某一点是否在某一区域上
    return CGRectContainsPoint(bounds, point);
}
@end

iOS课程观看笔记(一)---UI视图相关_第14张图片

点击红色区域,有打印:

2020-11-30 10:09:32.393002+0800 test001[1807:50672] -[ViewController btnClick]

这个容易理解,UIButton的点击区域扩大了,扩大到了父控件的宽高

现在,将UIButton的frame变为UIView的宽高,也就是都是(100, 100, 100, 100),此时,按照题意,要将UIButton的点击区域扩大2倍,也就是(200, 200)

- (void)viewDidLoad {
     
    [super viewDidLoad];
    
    UIView *redView = [[UIView alloc] init];
    redView.frame = CGRectMake(100, 100, 100, 100);
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];
    
    YZButton *btn = [[YZButton alloc] init];
    btn.frame = CGRectMake(0, 0, 100, 100);
    btn.backgroundColor = [UIColor yellowColor];
    [btn addTarget:self action:@selector(btnClick) forControlEvents:UIControlEventTouchUpInside];
    [redView addSubview:btn];
}


#import "YZButton.h"
@implementation YZButton
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
     
    
    CGRect bounds = self.bounds;
    //扩大原热区直径至200,可以暴露个接口,用来设置需要扩大的半径。
    CGFloat widthDelta = MAX(200, 0);
    CGFloat heightDelta = MAX(200, 0);
    bounds = CGRectInset(bounds, -0.5 * widthDelta, -0.5 * heightDelta);
    return CGRectContainsPoint(bounds, point);
}
@end

iOS课程观看笔记(一)---UI视图相关_第15张图片

此时,点击UIButton外部的区域,是没有点击事件的,原因就是那个错误答案,点击point超出其父控件的点击区域,因此,不能响应事件。

然而,既然UIButton可以扩大点击区域,UIView也可以扩大点击区域,因此,重写UIView的-(BOOL)pointInside:withEvent:方法:

#import "YZView.h"
@implementation YZView
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
     
    CGRect bounds = self.bounds;
    //扩大原热区直径至200,可以暴露个接口,用来设置需要扩大的半径。
    CGFloat widthDelta = MAX(200, 0);
    CGFloat heightDelta = MAX(200, 0);
    bounds = CGRectInset(bounds, -0.5 * widthDelta, -0.5 * heightDelta);
    return CGRectContainsPoint(bounds, point);
}
@end

神奇的一幕出现了,点击UIButton的外部区域,也可以打印

总结:

之前的错误答案在于,只想到了父控件不能响应事件,没有想到可以同时扩大父控件的点击区域。因此,可以通过-(BOOL)pointInside:withEvent:方法来扩大UIButton的点击区域,只需要将其父控件也扩大即可。


事件响应链流程

响应者对象(UIResponder)

学习触摸事件首先要了解一个比较重要的概念-响应者对象(UIResponder)。

在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接受并处理事件,我们称之为“响应者对象”。
iOS课程观看笔记(一)---UI视图相关_第16张图片
UIView继承UIResponder,UIResponder里面有四个响应方法

//手指触碰屏幕,触摸开始
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//手指在屏幕上移动
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//手指离开屏幕,触摸结束
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//触摸结束前,某个系统事件中断了触摸,例如电话呼入
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;

UIView的继承关系
UIView : UIResponder : NSObject

UIButton的继承关系
UIButton : UIControl : UIView : UIResponder : NSObject

UIWindow : UIView
UIApplication : UIResponder
UIViewController : UIResponder(查代码可知)

iOS课程观看笔记(一)---UI视图相关_第17张图片

UIControl里面有四个方法:

- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)event;
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)event;
- (void)endTrackingWithTouch:(nullable UITouch *)touch withEvent:(nullable UIEvent *)event; // touch is sometimes nil if cancelTracking calls through to this.
- (void)cancelTrackingWithEvent:(nullable UIEvent *)event; 

iOS课程观看笔记(一)---UI视图相关_第18张图片

事件响应链

事件的产生和传递

  • 发送触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中
  • UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)
  • 主窗口会在视图层次结构中,找到一个最合适的视图来处理触摸事件
  • 找到合适的视图控制后,就会调用视图空间的touches方法来作具体的事件处理

触摸事件的传递是从父控件传递到子控件
也就是UIApplication->window->寻找处理事件最合适的view

事件的响应

响应者链的事件传递过程

  1. 如果当前view是控制器的view,则传给控制器;如果当前view不是控制器的view,则传递给该view的父视图
  2. 在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,则其将事件或消息传递给window对象进行处理
  3. 如果window对象也不处理,则其将事件或消息传递给UIApplication对象
  4. 如果UIApplication也不能处理该事件或消息,则将其丢弃

谁是上一个响应者?

  1. 如果当前这个view是控制器的view,那么控制器就是上一个响应者
  2. 如果当前这个view不是控制器的view,那么其父控制器就是上一个响应者

问:如何通过一个view,找到它所属的控制器UIController?

这道题一开始想的是,调用view.superView,最终可以找到UIController所属的view,现在的问题就是,如何通过UIController的所属view,找到其控制器。因为view是UIController的一个属性,因此,也可以理解为:如何通过属性,找到类对象

但是,结合响应链的传递,以及事件处理传递,我们可以知道,通过响应链,可以根据view找到所属控制器,然后调用控制器的某些方法,去处理事情。
也就是UIView继承的UIResponder跟控制器是否存在某种关系?

响应链中的事件传递规则:
每一个响应者对象(UIResponder对象)都有一个 nextResponder 方法,用于获取响应链中当前对象的下一个响应者。因此,一旦事件的最佳响应者确定了,这个事件所处的响应链就确定了。

对于响应者对象,默认的 nextResponder 实现如下:

  • UIView 若视图是控制器的根视图,则其nextResponder为控制器对象;否则,其nextResponder为父视图。
  • UIViewController 若控制器的视图是window的根视图,则其nextResponder为窗口对象;若控制器是从别的控制器present出来的,则其nextResponder为presenting view controller。
  • UIWindow nextResponder为UIApplication对象。
  • UIApplication 若当前应用的app delegate是一个UIResponder对象,且不是UIView、UIViewController或app本身,则UIApplication的nextResponder为app delegate。

根据第一条,UIView的nextResponder要不是控制器,要不是superView
因此,可以得出以下代码:

- (UIViewController *)findViewController:(UIView *)sourceView
{
     
    id target=sourceView;
    while (target) {
     
        target = ((UIResponder *)target).nextResponder;
        if ([target isKindOfClass:[UIViewController class]]) {
     
            break;
        }
    }
    return target;
}

参考:
通过UIView获取UIViewController
iOS 触摸事件 hitTest touches nextResponder


问:如果最后到AppDelegate都没有接收事件,会发生什么?

该事件会被抛弃,什么也不会发生。


问UIButton添加一个addTarget事件(UIControl),一个手势识别器UIGestureRecognizer,还有UIResponder下面的touchBegin监听,UIButton会如何响应?

也就是UIGestureRecognizer、UIResponder、UIControl三者的关系

首先,我们先了解几个知识点:


系统类的UIControl(UIButton、UISwitch等)的响应优先级比其父视图上的手势识别器高
而自定义的UIControl,响应优先级比其父视图上的手势识别器低。

意思就是,如果一个UIView有一个手势识别器事件,UIView上有一个UIButton,UIButton有一个addTarget事件,点击UIButton,UIButton会先响应addTarget事件。

自定义YZButton,在里面做如下操作:

#import "YZButton.h"

@implementation YZButton
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
     
    [super touchesBegan:touches withEvent:event];
    NSLog(@"%s", __func__);
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
     
    [super touchesMoved:touches withEvent:event];
    NSLog(@"%s", __func__);
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
     
    [super touchesEnded:touches withEvent:event];
    NSLog(@"%s", __func__);
}

- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
     
    [super touchesCancelled:touches withEvent:event];
    NSLog(@"%s", __func__);
}

- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)event
{
     
    NSLog(@"%s", __func__);
    return [super beginTrackingWithTouch:touch withEvent:event];
}

- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)event
{
     
    NSLog(@"%s", __func__);
    return [super continueTrackingWithTouch:touch withEvent:event];
}

- (void)endTrackingWithTouch:(nullable UITouch *)touch withEvent:(nullable UIEvent *)event
{
     
    [super endTrackingWithTouch:touch withEvent:event];
    NSLog(@"%s", __func__);
}

- (void)cancelTrackingWithEvent:(nullable UIEvent *)event
{
     
    [super cancelTrackingWithEvent:event];
    NSLog(@"%s", __func__);
}
@end

然后

YZButton *btn = [[YZButton alloc] init];
btn.frame = CGRectMake(0, 0, 200, 200);
btn.backgroundColor = [UIColor redColor];
[self.view addSubview:btn];

[btn addTarget:self action:@selector(actionClick) forControlEvents:UIControlEventTouchUpInside];

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapClick)];
[self.view addGestureRecognizer:tap];

打印结果:
-[YZButton beginTrackingWithTouch:withEvent:]
-[YZButton touchesBegan:withEvent:]
-[YZButton endTrackingWithTouch:withEvent:]
actionClick
-[YZButton touchesEnded:withEvent:]

手势识别器UIGestureRecognizer比UIResponder具有更高的事件响应优先级,也就是
手势识别器UIGestureRecognizer > UIResponder(touchBegin)

YZButton *btn = [[YZButton alloc] init];
btn.frame = CGRectMake(0, 0, 200, 200);
btn.backgroundColor = [UIColor redColor];
[self.view addSubview:btn];

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapClick)];
[btn addGestureRecognizer:tap];

打印结果:
-[YZButton beginTrackingWithTouch:withEvent:]
-[YZButton touchesBegan:withEvent:]
tapClick
-[YZButton cancelTrackingWithEvent:]
-[YZButton touchesCancelled:withEvent:]

怎么从结果发现,好像是UIResponder比UIGestureRecognizer先调用呢???
我们搞一个自定义的YZTapGestureRecognizer,其继承UITapGestureRecognizer

#import "YZTapGestureRecognizer.h"

@implementation YZTapGestureRecognizer
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
     
    [super touchesBegan:touches withEvent:event];
    NSLog(@"%s", __func__);
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
     
    [super touchesMoved:touches withEvent:event];
    NSLog(@"%s", __func__);
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
     
    [super touchesEnded:touches withEvent:event];
    NSLog(@"%s", __func__);
}

- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
     
    [super touchesCancelled:touches withEvent:event];
    NSLog(@"%s", __func__);
}
@end

然后,执行

YZButton *btn = [[YZButton alloc] init];
btn.frame = CGRectMake(0, 0, 200, 200);
btn.backgroundColor = [UIColor redColor];
[self.view addSubview:btn];

YZTapGestureRecognizer *tap = [[YZTapGestureRecognizer alloc] initWithTarget:self action:@selector(tapClick)];
[btn addGestureRecognizer:tap];

打印结果:
-[YZTapGestureRecognizer touchesBegan:withEvent:]
-[YZButton beginTrackingWithTouch:withEvent:]
-[YZButton touchesBegan:withEvent:]
-[YZTapGestureRecognizer touchesEnded:withEvent:]
tapClick
-[YZButton cancelTrackingWithEvent:]
-[YZButton touchesCancelled:withEvent:]

从结果可以看出,确实是
手势识别器UIGestureRecognizer > UIResponder(touchBegin)


重点来了

三个方法全部打开

YZButton *btn = [[YZButton alloc] init];
btn.frame = CGRectMake(0, 0, 200, 200);
btn.backgroundColor = [UIColor redColor];
[self.view addSubview:btn];

[btn addTarget:self action:@selector(actionClick) forControlEvents:UIControlEventTouchUpInside];

YZTapGestureRecognizer *tap = [[YZTapGestureRecognizer alloc] initWithTarget:self action:@selector(tapClick)];
[btn addGestureRecognizer:tap];

打印结果:
-[YZTapGestureRecognizer touchesBegan:withEvent:]
-[YZButton beginTrackingWithTouch:withEvent:]
-[YZButton touchesBegan:withEvent:]
-[YZTapGestureRecognizer touchesEnded:withEvent:]
tapClick
-[YZButton cancelTrackingWithEvent:]
-[YZButton touchesCancelled:withEvent:]

结果分析:
首先是UIGestureRecognizer的手势识别器先接收事件
然后是UIResponder的touchBegin
由于[YZButton cancelTrackingWithEvent:]和[YZButton touchesCancelled:withEvent:]被调用,等于YZButton已经取消事件的接收,也就不会触发UIController的actionClick方法
也就是说:手势识别器调用完毕后,直接调用了touchesCancelled,使得actionClick不会触发

手势识别器UIGestureRecognizer > UIResponder(touchBegin)>UIController

更多学习有关事件传递与响应链
iOS触摸事件全家桶


图像显示原理

iOS课程观看笔记(一)---UI视图相关_第19张图片
iOS课程观看笔记(一)---UI视图相关_第20张图片
iOS课程观看笔记(一)---UI视图相关_第21张图片
Layout:UI布局、文本计算
Display:绘制,drawRect方法
Prepare:图片编解码
Commit:提交位图

iOS课程观看笔记(一)---UI视图相关_第22张图片

顶点着色器就是处理顶点相关的信息,片段着色器就是处理画面的颜色信息。


UI卡顿、掉帧的原因

iOS课程观看笔记(一)---UI视图相关_第23张图片

在规定时间内(16.7ms)内,CPU和GPU没有做好准备,在VSync来临时,图片不能显示,造成卡顿、掉帧
人的大脑眼睛在1秒钟内接收至少60帧的画面,就是流畅的效果,也就是60FPS
1s/60 = 1000ms/60 约等于16.7ms

滑动优化方案

可以分别从CPU和GPU两方面着手

CPU
对象创建、跳转、销毁。放在子线程
预排版(布局计算、文本计算)放在子线程
预渲染(文本等异步绘制、图片编解码等)

GPU:
纹理渲染:减少离屏渲染
视图混合:尽可能的减少多层的View混合

UIView的绘制原理

iOS课程观看笔记(一)---UI视图相关_第24张图片

[UIView setNeedsDisplay]这行代码执行调用,并不会立马进行UI绘制

[UIView setNeedsDisplay]会立刻调用layer的同名函数setNeedsDisplay

在当前runloop即将进行休眠的时候(kCFRunLoopBeforeWaiting),setNeedsDisplay才真正调用[CALayer display],然后进行UIView的绘制

接下来,我们看下系统绘制流程异步绘制流程
系统绘制流程
iOS课程观看笔记(一)---UI视图相关_第25张图片

异步绘制流程
异步绘制,就是异步在画布上绘制内容。
在子线程中绘制Core Graphic对象,最后再回到主线程中设置layer.contents内容。
iOS课程观看笔记(一)---UI视图相关_第26张图片iOS课程观看笔记(一)---UI视图相关_第27张图片


离屏渲染

iOS课程观看笔记(一)---UI视图相关_第28张图片

离屏渲染何时触发?

1圆角:
view.layer.cornerRadius = 5.0;和view.layer.masksToBounds = YES;必须同时执行才会触发。

2图层蒙版
3阴影
4光栅化
光栅化概念:将图转化为一个个栅格组成的图象。
光栅化特点:每个元素对应帧缓冲区中的一像素。

为何要避免离屏渲染

离屏渲染会增加GPU的工作量,使得GPU处理时间变长,从而使得在屏幕显示中16.7ms中没有完成工作,造成UI卡顿、掉帧现象。
增加的工作量包括:
创建新的渲染缓冲区
上下文切换


layoutsubviews在什么时候调用

一般baidu出来的答案如下,然而说明并不够透彻,在此补充说明:
1、init初始化不会触发layoutSubviews
2、addSubview会触发layoutSubviews
3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化,xy改动不管,wh改动管
4、滚动一个UIScrollView会触发layoutSubviews
5、旋转Screen会触发父UIView上的layoutSubviews事件
6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件

首先:layoutSubviews 字面意思是: 布局子控件,也就是说改变子控件会调用父类该方法;
1、init初始化不会触发layoutSubviews,
这点确实不会调用;
2、addSubview会触发layoutSubviews,
如果添加的子控件没有Frame,不会调用;
3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化;
还有一个前提是该View 已经被添加到父控件, 此时View和其父控件的layoutSubviews都会调用;
也就包含了6 的情况
4、滚动一个UIScrollView会触发layoutSubviews ,因滚动UIScrollView,其子控件肯定对应会刷新,也就肯定会被调用;
这点会调用;
5、旋转Screen会触发控制器对应UIView上的layoutSubviews事件
做一点更正;
总结:改变子控件就会调用父类的方法;

参考:
layoutSubviews 调用时机

你可能感兴趣的:(iOS课程观看笔记)