一、YYLabel實現的總體思路
其實YYLabel主要就是為了實現異步繪圖,提高性能,為此做了好多東西。
1、使用自定義的Layer,YYAsyncLayer
2、在YYAsyncLayer中實現異步繪圖,因為系統默認的繪圖都是在主線程執行了的,這里要實現異步繪圖,那么就得擯棄掉系統的繪圖方法,自己去實現。所以這里得花費很多功夫。所以說,提高性能是要付出很多的。
二、下面開始進入代碼分析:
(1)使用自定義的Layer,YYAsyncLayer
在YYLabel中重寫了下面的方法:
+ (Class)layerClass {
???return [YYAsyncLayer class];
}
就是修改了View的主Layer層,把YYAsyncLayer的實例作為主Layer層
(2)View與Layer的關系
View其實就是Layer的代理,Layer是View的底層實現,View是Layer的管理類。繪圖和坐標等操作都是在Layer中實現的,View只是訪問Layer中的相關方法。
(3)系統View的繪圖過程:
1、調用setNeedsDisplay
2、調用Layer中的display方法
3、在display中會調用Layer的drawInContext方法
4、在drawInContext中會調用代理方法drawLayer:(CALayer *)layer
inContext:(CGContextRef)ctx,這就進入到View了
5、最后View在代理實現中調用了drawRect方法,這方法就是我們經常用到了那個。
我寫了demo驗證了上面的過程,結果如下:
(4) YYLabe的繪圖過程
在YYAsyncLayer中,我們找到display方法:
- (void)display {
???super.contents = super.contents;
???[self _displayAsync:_displaysAsynchronously];
}
重寫了display方法,但沒有調用super方法,就是說上面的流程已經被打斷了,那么view的系統繪圖方法都不會被調用。
通過代理方法newAsyncDisplayTask得到了YYAsyncLayerDisplayTask類實例,再通過調用類實例的block方法直接返回到View中進行操作。作者打斷了之前的繪圖流程,但這里是通過block進入到UIView中進行繪圖操作,這樣就可以完全控制繪圖過程。
在_displayAsync方法中:
可見,這里是在線程中操作的,先創建了上下文context,然后再調用代理方法:task.display(context, size,isCancelled),所以UIView在代理實現中的繪圖操作都是在線程中執行的,這里就實現了異步繪圖。
(5) newAsyncDisplayTask的代理實現
上面是得到一些繪圖需要的參數,其中最主要的是YYTextLayout對象,里面包含了很多繪圖需要的信息。
然后是block display方法:
上面提到這個block實在非主線程執行的,這里執行了真正的繪圖操作,繪圖操作方法:
[drawLayoutdrawInContext:context size:size point:point view:nil layer:nil debug:debug cancel:isCancelled];中。
具體的繪圖還是挺復雜了,看起來有點吃力,以后有機會再研究下。好了,整個YYLabel的實現流程已經分析完成,是不是覺得源碼作者很牛逼。作者為了實現異步繪圖,花了好多的功夫。
總結:
所以整體的流程是:首先得理解CALayer與UIView的關系,UIView就是CALayer的代理,CALayer實現真正的繪制工作,繪制好之后,再通過代理傳遞到UIView的drawRect方法,給UIView進一步繪制的工作,不過UIView也基本不需要在drawRect做什么東西。
YYkit利用這個原理。自定義了自己的YYLayer和YYLabel,通過在YYLabel上的layerClass關聯YYLayer。因為不能使用CALayer的繪制代碼(UIKit那套是不能在子線程執行),所有必須打斷掉以前CALayer的執行順序(不讓它使用之前的繪制)。YYKit在YYLayer上重寫了display方法,打斷了以前的順序,讓它不去執行之前的繪制。接著在display方法里,通過代理從YYLabel得到一個task,YYLabel實現task.display()。YYLayer創建一個上下文畫板,然后通過在子線程調用task.display(context)傳遞畫板-context給YYLabel,YYLabel在畫板上用coreGraphic繪制正在的內容。繪制好后,在YYLayer上通過主線程返回給layer的self.contents,畫面就展示出來了。