事件處理
事件產(chǎn)生
- 當(dāng)用戶點(diǎn)擊屏幕,IOKit收到屏幕操作灶壶,將這次操作封裝為IOHIDEvent對象陕靠,通過mach port將事件發(fā)送給SpringBoard。
- SpringBoard是iOS系統(tǒng)的桌面程序鞠评,SpringBoard收到mach port發(fā)送來的事件茂蚓,將事件發(fā)送給在前臺顯示的程序。
- 喚醒此程序的main runloop并交給source1處理剃幌,處理內(nèi)部會將事件交給source0處理聋涨,并調(diào)用source0的
__UIApplicationHandleEventQueue()
函數(shù),在其中將事件轉(zhuǎn)換為UIEvent负乡。 - 調(diào)用UIApplocation的sendEvent:方法
事件傳遞&響應(yīng)
傳遞:
- UIApplication接收到事件牛郑,將事件傳遞給keyWindow。
- keyWindow遍歷subViews的hitTest:withEvent:方法敬鬓,找到點(diǎn)擊區(qū)域合適的試圖來處理淹朋。
- UIView的子視圖也會遍歷其subViews的hitTest:withEvent:方法。
- 找到點(diǎn)擊區(qū)域內(nèi)钉答,且處于最上方的視圖础芍,將視圖逐步返回給UIApplication。
- 在查找第一響應(yīng)者的過程中数尿,形成了響應(yīng)鏈仑性。
響應(yīng):
- 應(yīng)用調(diào)用第一響應(yīng)者處理事件,
- 如果第一響應(yīng)者不能處理事件右蹦,則調(diào)用其nextResponder方法诊杆,一直找到響應(yīng)鏈中能夠處理該事件的對象歼捐。
- 最后到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;
}
事件攔截
如果想讓指定試圖來響應(yīng)事件晨汹,不再向其子試圖繼續(xù)傳遞事件豹储,可以通過重寫如下
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
return self;
}
事件轉(zhuǎn)發(fā)
SuperView的SubView超出了其視圖范圍,如果點(diǎn)擊SubView在試圖外面的部分則不能響應(yīng)時間淘这“郏可以通過重寫pointInside:withEvent:方法,將相應(yīng)區(qū)域擴(kuò)大為虛線區(qū)域铝穷,包括SuperView的所有子試圖钠怯,即可以讓子試圖響應(yīng)事件。
事件逐級傳遞
如果想讓響應(yīng)者中每一級都可以響應(yīng)事件曙聂,可以在每級UIResponder中都實(shí)現(xiàn)touches并調(diào)用super晦炊。
UIControl > 手勢 > UIResponder
iOS渲染原理 & UI的刷新原理
CPU 和 GPU 的設(shè)計(jì)目的分別是什么?
CPU是運(yùn)算核心和控制核心宁脊,需要很強(qiáng)的運(yùn)算通用型刽锤,兼容各種數(shù)據(jù)類型。
GPU則面對的是類型統(tǒng)一朦佩,更大的運(yùn)算任務(wù)并思。
計(jì)算機(jī)圖像渲染流水線的大致流程是什么?
Application(應(yīng)用處理階段:得到圖元) -> Geometry Processing(幾何處理階段:處理圖元) -> Rasterization(光柵化階段:圖元轉(zhuǎn)換為像素) -> Pixel Processing(處理像素语稠,得到位圖)
Framebuffer 幀緩沖器的作用是什么宋彼?
存儲渲染之后的像素信息,方便控制器讀取仙畦。
Screen Tearing 屏幕撕裂是怎么造成的输涕?
控制器讀取幀緩存器上半部時,新的幀以及渲染完成放入幀緩存器中慨畸,控制器讀取的不是同一幀圖像莱坎,導(dǎo)致屏幕撕裂。
如何解決屏幕撕裂的問題寸士?
引入垂直同步Vsync+雙緩沖機(jī)制Double Buffering
在屏幕掃描上一幀加載完成之前新渲染的幀數(shù)據(jù)放入back buffer里檐什,當(dāng)當(dāng)前幀加載完成后發(fā)出Vsync信號去通知控制器去將back buffer中的內(nèi)容置換到frame buffer中。
掉幀是怎么產(chǎn)生的弱卡?
如果在接收到Vsync信號時CPU和GPU還沒有渲染好新的位圖乃正,控制器就不會去替換frame buffer中的位圖。這時候屏幕就會重新掃描呈現(xiàn)出上一幀一摸一樣的畫面婶博,這就是掉幀瓮具。
CoreAnimation 的職責(zé)是什么?
Core Animation是一個復(fù)合引擎,主要職責(zé)包括:渲染 構(gòu)建 實(shí)現(xiàn)動畫名党,盡可能快的組合屏幕上的CALayer叹阔,并且被存儲為樹狀層級結(jié)構(gòu)
UIView 和 CALayer 是什么關(guān)系?有什么區(qū)別传睹?
創(chuàng)建UIView都會自動創(chuàng)建一個CALayer耳幢,為自身提供存儲bitmap的地方,并將自身固定設(shè)置為CALayer的代理蒋歌。
- 我們對 UIView 的層級結(jié)構(gòu)非常熟悉,由于每個 UIView 都對應(yīng) CALayer 負(fù)責(zé)頁面的繪制委煤,所以 CALayer 也具有相應(yīng)的層級結(jié)構(gòu)堂油。
- CALayer 繼承自 NSObject,UIView 由于要負(fù)責(zé)交互事件碧绞,所以繼承自 UIResponder府框。
- 因?yàn)?UIView 只對 CALayer 的部分功能進(jìn)行了封裝,而另一部分如圓角讥邻、陰影迫靖、邊框等特效都需要通過調(diào)用 layer 屬性來設(shè)置。
- CALayer 不負(fù)責(zé)點(diǎn)擊事件兴使,所以不響應(yīng)點(diǎn)擊事件系宜,而 UIView 會響應(yīng)。
為什么會同時有 UIView 和 CALayer发魄,能否合成一個盹牧?
- 這樣設(shè)計(jì)的主要原因就是為了職責(zé)分離,拆分功能励幼,方便代碼的復(fù)用汰寓。
- iOS 有 UIKit 和 UIView,OS X 則是AppKit 和 NSView苹粟。
渲染流水線中有滑,CPU 會負(fù)責(zé)哪些任務(wù)?
離屏渲染為什么會有效率問題嵌削?
離屏渲染需要先額外創(chuàng)建離屏緩沖區(qū)毛好,將提前渲染好的內(nèi)容放入,之后將Offscreen Buffer中的內(nèi)容進(jìn)一步疊加 渲染 完成后將結(jié)果切換到FrameBuffer中苛秕。
- Offscreen Buffer本身需要額外空間睛榄。
- 離屏渲染的開銷很大,一旦離屏渲染的內(nèi)容過多想帅,很容易造成掉幀场靴。
什么時候應(yīng)該使用離屏渲染?
- 一些特殊效果需要額外使用Offscreen Buffer來保存渲染的中間狀態(tài)。(系統(tǒng)自動觸發(fā):陰影 圓角)
- 效率目的旨剥,可以將內(nèi)容提前渲染保存在Offscreen Buffer中咧欣,達(dá)到復(fù)用的目的。(主動觸發(fā):通過CALayer shouldRasterize)
shouldRasterize 光柵化是什么轨帜?
開啟光柵化后魄咕,會觸發(fā)離屏渲染,Render Server會強(qiáng)制將CALayer的渲染位圖結(jié)果bitmap保存起來蚌父,下次可直接復(fù)用哮兰。
有哪些常見的觸發(fā)離屏渲染的情況?
- 使用了mask的layer(layer.mask)
- 需要進(jìn)行裁剪的layer(layer.masksToBounds/view.clipsToBounds)
- 設(shè)置了組透明度為YES苟弛,并且透明度不為1的layer(layer.opacity)
- 添加了投影的layer(layer.shadow)
- 采用了光柵化的layer(layer.shouldRasterize)
- 繪制了文字的layer(UILabel, CATextLayer, Core Text)
圓角觸發(fā)的離屏渲染有哪些解決方案喝滞?
- 使用帶圓角的圖片。
- 在添加一個和背景相同的遮罩mask覆蓋在最上面膏秫,蓋住四個角右遭。
- UIBezierPath貝塞爾曲線。
- CoreGraphics缤削,重寫drawRect
重寫 drawRect 方法會觸發(fā)離屏渲染嗎窘哈?
不會,雖然drawRect會將GPU中的渲染操作放入CPU中來做亭敢,并且需要額外開辟一個空間來做滚婉,這和標(biāo)準(zhǔn)意義上的離屏渲染不同。
drawrect & layoutsubviews調(diào)用時機(jī)
layoutsubviews:
- addSubView
- 修改frame
- 滾動UISCrollView
- 直接調(diào)用setNeedsLayout
drawrect:
- Controller->loadView 和 -> viewDidLoad之后
- sizeToFit之后
- 設(shè)置contentMode屬性值為UIViewContentModeRedraw帅刀。那么將在每次設(shè)置或更改frame的時候自動調(diào)用drawRect:
- 直接調(diào)用setNeedsDisplay
Talbleview 的性能優(yōu)化
- 快速滑動時先不加載TalbleviewCell上的圖片满哪,等將要停止時再去加載當(dāng)前Cell附近Cell的圖片。
- 監(jiān)聽RunLoop閑時再去做圖片的加載劝篷。
AutoLayout的原理哨鸭,性能如何
根據(jù)約束條件算出frame,在復(fù)雜界面異常差娇妓。
隱式動畫 & 顯示動畫區(qū)別
imageName & imageWithContentsOfFile區(qū)別
imageName:
- 系統(tǒng)會緩存加載的圖片
imageWithContentsOfFile:
- 不會被緩存像鸡,每次都需要重新加載