事件处理
事件产生
- 当用户点击屏幕,IOKit收到屏幕操作,将这次操作封装为IOHIDEvent对象,通过mach port将事件发送给SpringBoard。
- SpringBoard是iOS系统的桌面程序,SpringBoard收到mach port发送来的事件,将事件发送给在前台显示的程序。
- 唤醒此程序的main runloop并交给source1处理,处理内部会将事件交给source0处理,并调用source0的
__UIApplicationHandleEventQueue()
函数,在其中将事件转换为UIEvent。 - 调用UIApplocation的sendEvent:方法
事件传递&响应
传递:
- UIApplication接收到事件,将事件传递给keyWindow。
- keyWindow遍历subViews的hitTest:withEvent:方法,找到点击区域合适的试图来处理。
- UIView的子视图也会遍历其subViews的hitTest:withEvent:方法。
- 找到点击区域内,且处于最上方的视图,将视图逐步返回给UIApplication。
- 在查找第一响应者的过程中,形成了响应链。
响应:
- 应用调用第一响应者处理事件,
- 如果第一响应者不能处理事件,则调用其nextResponder方法,一直找到响应链中能够处理该事件的对象。
- 最后到UIApplication后仍然没有处理该事件的对象,则该事件被废弃
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (self.alpha <= 0.01 || self.userInteractionEnabled == NO || self.hidden) {
return nil;
}
BOOL inside = [self pointInside:point withEvent:event];
if (inside) {
NSArray *subViews = self.subviews;
// 对子视图从上向下找
for (NSInteger i = subViews.count - 1; i >= 0; i--) {
UIView *subView = subViews[i];
CGPoint insidePoint = [self convertPoint:point toView:subView];
UIView *hitView = [subView hitTest:insidePoint withEvent:event];
if (hitView) {
return hitView;
}
}
return self;
}
return nil;
}
事件拦截
如果想让指定试图来响应事件,不再向其子试图继续传递事件,可以通过重写如下
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
return self;
}
事件转发
SuperView的SubView超出了其视图范围,如果点击SubView在试图外面的部分则不能响应时间。可以通过重写pointInside:withEvent:方法,将相应区域扩大为虚线区域,包括SuperView的所有子试图,即可以让子试图响应事件。
事件逐级传递
如果想让响应者中每一级都可以响应事件,可以在每级UIResponder中都实现touches并调用super。
UIControl > 手势 > UIResponder
iOS渲染原理 & UI的刷新原理
CPU 和 GPU 的设计目的分别是什么?
CPU是运算核心和控制核心,需要很强的运算通用型,兼容各种数据类型。
GPU则面对的是类型统一,更大的运算任务。
计算机图像渲染流水线的大致流程是什么?
Application(应用处理阶段:得到图元) -> Geometry Processing(几何处理阶段:处理图元) -> Rasterization(光栅化阶段:图元转换为像素) -> Pixel Processing(处理像素,得到位图)
Framebuffer 帧缓冲器的作用是什么?
存储渲染之后的像素信息,方便控制器读取。
Screen Tearing 屏幕撕裂是怎么造成的?
控制器读取帧缓存器上半部时,新的帧以及渲染完成放入帧缓存器中,控制器读取的不是同一帧图像,导致屏幕撕裂。
如何解决屏幕撕裂的问题?
引入垂直同步Vsync+双缓冲机制Double Buffering
在屏幕扫描上一帧加载完成之前新渲染的帧数据放入back buffer里,当当前帧加载完成后发出Vsync信号去通知控制器去将back buffer中的内容置换到frame buffer中。
掉帧是怎么产生的?
如果在接收到Vsync信号时CPU和GPU还没有渲染好新的位图,控制器就不会去替换frame buffer中的位图。这时候屏幕就会重新扫描呈现出上一帧一摸一样的画面,这就是掉帧。
CoreAnimation 的职责是什么?
Core Animation是一个复合引擎,主要职责包括:渲染 构建 实现动画,尽可能快的组合屏幕上的CALayer,并且被存储为树状层级结构
UIView 和 CALayer 是什么关系?有什么区别?
创建UIView都会自动创建一个CALayer,为自身提供存储bitmap的地方,并将自身固定设置为CALayer的代理。
- 我们对 UIView 的层级结构非常熟悉,由于每个 UIView 都对应 CALayer 负责页面的绘制,所以 CALayer 也具有相应的层级结构。
- CALayer 继承自 NSObject,UIView 由于要负责交互事件,所以继承自 UIResponder。
- 因为 UIView 只对 CALayer 的部分功能进行了封装,而另一部分如圆角、阴影、边框等特效都需要通过调用 layer 属性来设置。
- CALayer 不负责点击事件,所以不响应点击事件,而 UIView 会响应。
为什么会同时有 UIView 和 CALayer,能否合成一个?
- 这样设计的主要原因就是为了职责分离,拆分功能,方便代码的复用。
- iOS 有 UIKit 和 UIView,OS X 则是AppKit 和 NSView。
渲染流水线中,CPU 会负责哪些任务?
离屏渲染为什么会有效率问题?
离屏渲染需要先额外创建离屏缓冲区,将提前渲染好的内容放入,之后将Offscreen Buffer中的内容进一步叠加 渲染 完成后将结果切换到FrameBuffer中。
- Offscreen Buffer本身需要额外空间。
- 离屏渲染的开销很大,一旦离屏渲染的内容过多,很容易造成掉帧。
什么时候应该使用离屏渲染?
- 一些特殊效果需要额外使用Offscreen Buffer来保存渲染的中间状态。(系统自动触发:阴影 圆角)
- 效率目的,可以将内容提前渲染保存在Offscreen Buffer中,达到复用的目的。(主动触发:通过CALayer shouldRasterize)
shouldRasterize 光栅化是什么?
开启光栅化后,会触发离屏渲染,Render Server会强制将CALayer的渲染位图结果bitmap保存起来,下次可直接复用。
有哪些常见的触发离屏渲染的情况?
- 使用了mask的layer(layer.mask)
- 需要进行裁剪的layer(layer.masksToBounds/view.clipsToBounds)
- 设置了组透明度为YES,并且透明度不为1的layer(layer.opacity)
- 添加了投影的layer(layer.shadow)
- 采用了光栅化的layer(layer.shouldRasterize)
- 绘制了文字的layer(UILabel, CATextLayer, Core Text)
圆角触发的离屏渲染有哪些解决方案?
- 使用带圆角的图片。
- 在添加一个和背景相同的遮罩mask覆盖在最上面,盖住四个角。
- UIBezierPath贝塞尔曲线。
- CoreGraphics,重写drawRect
重写 drawRect 方法会触发离屏渲染吗?
不会,虽然drawRect会将GPU中的渲染操作放入CPU中来做,并且需要额外开辟一个空间来做,这和标准意义上的离屏渲染不同。
drawrect & layoutsubviews调用时机
layoutsubviews:
- addSubView
- 修改frame
- 滚动UISCrollView
- 直接调用setNeedsLayout
drawrect:
- Controller->loadView 和 -> viewDidLoad之后
- sizeToFit之后
- 设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:
- 直接调用setNeedsDisplay
Talbleview 的性能优化
- 快速滑动时先不加载TalbleviewCell上的图片,等将要停止时再去加载当前Cell附近Cell的图片。
- 监听RunLoop闲时再去做图片的加载。
AutoLayout的原理,性能如何
根据约束条件算出frame,在复杂界面异常差。
隐式动画 & 显示动画区别
imageName & imageWithContentsOfFile区别
imageName:
- 系统会缓存加载的图片
imageWithContentsOfFile:
- 不会被缓存,每次都需要重新加载
网友评论