UI相关
- 重用机制

- 通过一个自定义控件字母索引条讲解
- 数据源同步

- 并发访问,数据拷贝

- 串行访问

- 并发访问,数据拷贝
事件传递&视图响应
-
UIView和CALayer关系

- UIView为其提供内容,以及负责处理触摸等事件,参与响应链
- CALayer负责显示内容contents
- 设计目的是单一职责原则
-
事件传递机制

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
- 事件响应流程


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- 示例代码
//实现一个button,只有内切圆点击能响应点击事件
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if (!self.userInteractionEnabled ||
[self isHidden] ||
self.alpha <= 0.01) {
return nil;
}
if ([self pointInside:point withEvent:event]) {
//遍历当前对象的子视图
__block UIView *hit = nil;
[self.subviews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// 坐标转换
CGPoint vonvertPoint = [self convertPoint:point toView:obj];
//调用子视图的hittest方法
hit = [obj hitTest:vonvertPoint withEvent:event];
// 如果找到了接受事件的对象,则停止遍历
if (hit) {
*stop = YES;
}
}];
if (hit) {
return hit;
}
else{
return self;
}
}
else{
return nil;
}
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
CGFloat x1 = point.x;
CGFloat y1 = point.y;
CGFloat x2 = self.frame.size.width / 2;
CGFloat y2 = self.frame.size.height / 2;
//平方差公式,算出与圆心距离
double dis = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
// 67.923
if (dis <= self.frame.size.width / 2) {
return YES;
}
else{
return NO;
}
}
图像显示原理
-
一般来说,计算机系统中 CPU、GPU、屏幕是以上面这种方式协同工作的。CPU 计算好显示内容提交到 GPU,GPU 渲染完成后将渲染结果放入帧缓冲区,随后视频控制器会按照 VSync 信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给屏幕显示。


-
cpu工作
- layout
- UI布局
- 文本计算
- display
- 绘制
- prepare
- 图片编解码
- commit
- 提交位图
- layout
-
gpu渲染管线
- 顶点着色
- 图元装配
- 光栅化
- 片段着色
- 片段处理
-
cpu和gpu处理后提交到 FrameBuffer/帧缓存区
卡顿&掉帧

- 滑动优化方案
- cpu
- 对象创建、调整、销毁
- 预排版(布局、文本计算)
- 预渲染(文本等异步绘制,图片编解码等)
- gpu
- 纹理渲染:尽量减少在短时间内大量图片的显示,尽可能将多张图片合成为一张进行显示
- 视图混合:应用应当尽量减少视图数量和层次,并在不透明的视图里标明 opaque 属性 = YES,以避免GPU进行无用的 Alpha 通道合成
- 离屏渲染:layer的圆角、阴影、遮罩触发
- cpu
绘制原理&异步绘制
-
绘制原理,异步绘制入口

-
系统默认绘制流程

-
如何实现异步绘制

-
异步绘制核心示例代码
//- [layer.delegate displayLayer:]
//代理负责生成对应的bitmap
//设置该bitmap作为layer.contents属性的值
- (void)display {
dispatch_async(backgroundQueue, ^{
CGContextRef ctx = CGBitmapContextCreate(...);
// draw in context...
CGImageRef img = CGBitmapContextCreateImage(ctx);
CFRelease(ctx);
dispatch_async(mainQueue, ^{
layer.contents = img;
});
});
}
离屏渲染
- 什么是离屏渲染
- 在屏幕渲染,指的是gpu的渲染操作是在当前用于显示屏幕缓冲去中进行
- 离屏渲染,指的是gpu在当前屏幕缓冲区以外新开辟的一个缓冲区进行渲染操作
- 何时会触发离屏渲染
- 圆角(当和maskToBounds一起使用时)
- 图层蒙版
- 阴影
- 光栅化(CALayer.shouldRasterize 属性,但这会把原本离屏渲染的操作转嫁到 CPU 上去)
- 为何要避免
- UI卡顿和掉帧
- 创建新的渲染缓冲区
- 上下文切换
相关问题
- 系统的UI时间传递机制是怎样?
- 使UITableView滚动更流畅的方案或思路有哪些?
- 什么是离屏渲染?
- UIView和CALayer之间的关系是怎样的?