IOS Layer 簡析

第一部分:簡介

一、什麼是CALayer

     * 在iOS系統中,你能看得見摸得着的東西基本上都是UIView,比如一個按鈕、一個文本標籤、一個文本輸入框、一個圖標等等,這些都是UIView。

     * 其實UIView之所以能顯示在屏幕上,完全是因爲它內部的一個層。

     * 在創建UIView對象時,UIView內部會自動創建一個層(即CALayer對象),通過UIView的layer屬性可以訪問這個層。當UIView需要顯示到屏幕上時,會調用                  drawRect:方法進行繪圖,並且會將所有內容繪製在自己的層上,繪圖完畢後,系統會將層拷貝到屏幕上,於是就完成了UIView的顯示。

     * 換句話說,UIView本身不具備顯示的功能,是它內部的層纔有顯示功能。

二、UIView與CALayer的區別和聯繫

     * UIView是iOS系統中界面元素的基礎,所有的界面元素都繼承自它。它本身完全是由CoreAnimation來實現的(Mac下似乎不是這樣)。它真正的繪圖部分,是由一個叫CALayer(Core Animation Layer)的類來管理。UIView本身,更像是一個CALayer的管理器,訪問它的跟繪圖和跟座標有關的屬性,例如frame,bounds等等,實際上內部都是    在訪問它所包含的CALayer的相關屬性。

        就是說我們在操作UIView的一些跟繪圖和座標有關的屬性的時候,比如, self.view.backGround =[UIColor yellowColor] ,本質仍然是對CLayer做了操作. 由於代碼封裝我們看不到罷了.
     * UIView有個layer屬性,可以返回它的主CALayer實例,UIView有一個layerClass方法,返回主layer所使用的類,UIView的子類,可以通過重載這個方法,來讓UIView使用不同的CALayer來顯示,例如通過

[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. - (class) layerClass {  
  2.   return ([CAEAGLLayer class]);  
  3. }  
使某個UIView的子類使用GL來進行繪製。
     * UIView的CALayer類似UIView的子View樹形結構,也可以向它的layer上添加子layer,來完成某些特殊的表示。例如下面的代碼

[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. grayCover = [[CALayer alloc] init];  
  2. grayCover.backgroundColor = [[[UIColor blackColor] colorWithAlphaComponent:0.2] CGColor];  
  3. [self.layer addSubLayer: grayCover];  
  會在目標View上敷上一層黑色的透明薄膜。
     * UIView的layer樹形在系統內部,被系統維護着三份copy(這段理解有點喫不準)。
       第一份,邏輯樹,就是代碼裏可以操縱的,例如更改layer的屬性等等就在這一份。
       第二份,動畫樹,這是一箇中間層,系統正在這一層上更改屬性,進行各種渲染操作。
       第三份,顯示樹,這棵樹的內容是當前正被顯示在屏幕上的內容。
       這三棵樹的邏輯結構都是一樣的,區別只有各自的屬性。

     *動畫的運作
      UIView的主layer以外(我覺得是這樣),對它的subLayer,也就是子layer的屬性進行更改,系統將自動進行動畫生成,動畫持續時間有 個缺省時間,個人感覺大概是0.5秒。在動畫時間裏,系統自動判定哪些屬性更改了,自動對更改的屬性進行動畫插值,生成中間幀然後連續顯示產生動畫效果。

     *座標系系統(對position和anchorPoint的關係還是犯暈)
CALayer的座標系系統和UIView有點不一樣,它多了一個叫anchorPoint的屬性,它使用CGPoint結構,但是值域是0~1,也就是 按照比例來設置。這個點是各種圖形變換的座標原點,同時會更改layer的position的位置,它的缺省值是{0.5, 0.5},也就是在layer的中央。
某layer.anchorPoint = CGPointMake(0.f, 0.f);
如果這麼設置,layer的左上角就會被挪到原來的中間的位置,
加上這樣一句就好了
某layer.position = CGPointMake(0.f, 0.f);

     *layer可以設置圓角顯示,例如UIButton的效果,也可以設置陰影顯示,但是如果layer樹中的某個layer設置了圓角,樹中所有layer 的陰影效果都將顯示不了了。如果既想有圓角又想要陰影,好像只能做兩個重疊的UIView,一個的layer顯示圓角,一個的layer顯示陰 影.....
上面已經說過了,UIView之所以能夠顯示,完全是因爲內部的CALayer對象。因此,通過操作這個CALayer對象,可以很方便地調整UIView的一些界面屬性,比如:陰影、圓角大小、邊框寬度和顏色等。

三,CALayer的頭文件

[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. //CAEdgeAntialiasingMask的值  
  2. typedef NS_OPTIONS (unsigned int, CAEdgeAntialiasingMask){  
  3.     kCALayerLeftEdge      = 1U << 0,      /* Minimum X edge. */  
  4.     kCALayerRightEdge     = 1U << 1,      /* Maximum X edge. */  
  5.     kCALayerBottomEdge    = 1U << 2,      /* Minimum Y edge. */  
  6.     kCALayerTopEdge       = 1U << 3,      /* Maximum Y edge. */  
  7. };  
  8.   
  9. //==================Layer的創建和初始化  
  10. + (instancetype)layer;  
  11. - (instancetype)init;  
  12. - (instancetype)initWithLayer:(id)layer; //(待續。。。)  
  13. - (nullable id)presentationLayer;//是Layer的顯示層(呈現層),需要動畫提交之後纔會有值。  
  14. - (id)modelLayer;//模型層,在呈現圖層上調用–modelLayer將會返回它正在呈現所依賴的CALayer。通常在一個圖層上調用-modelLayer會返回–self  
  15. // 呈現層和模型層見http://www.360doc.com/showWeb/0/0/543080743.aspx#  
  16.    
  17. //===================屬性方法  
  18. /* 
  19.  CALayer的類或者其子類的所有ObjectiveC屬性都遵循了NSKeyValueCoding協議,對於子類聲明的屬性,它會動態的實現缺省的存取器方法。當使用KVC時,它的屬 
  20.  性不是一個對象類型,這時我們需要進行類型的轉換,這裏列舉了NSValue類的擴展,它支持下面的類型的轉換。基礎類型可以用NSNumber進行轉 
  21. */  
  22. // *      C Type                  Class  
  23. // *      ------                  -----  
  24. // *      CGPoint                 NSValue  
  25. // *      CGSize                  NSValue  
  26. // *      CGRect                  NSValue  
  27. // *      CGAffineTransform       NSAffineTransform  
  28. // *      CATransform3D           NSValue  */  
  29.   
  30. //返回這個屬性名所對應的屬性值的默認值,如果默認值是未知的,則返回nil,子類可以重載這個方法,來設定一些默認值。  
  31. + (nullable id)defaultValueForKey:(NSString *)key;  
  32. // 子類重載方法,當屬性改變(也包括通過動畫造成的layer的改變)需要重繪layer的內容時,返回YES。這個方法默認返回NO,不要通過CALayer返回YES,這樣會出現不定的錯誤。  
  33. + (BOOL)needsDisplayForKey:(NSString *)key;  
  34. //在調用-encodeWithCode方法時使用,表示某一屬性值是否可以歸檔。默認YES,可以歸檔。子類中需要對自定義的屬性歸檔的話,可以調用這個方法  
  35. - (BOOL)shouldArchiveValueForKey:(NSString *)key;  
  36.   
  37. //===================集合屬性和層級屬性  
  38. //層的邊界,默認爲CGRectZero。支持動畫。  
  39. @property CGRect bounds;  
  40.   
  41. //層的界定,用於界定在父層中的位置,默認值零點zero point,支持動畫  
  42. @property CGPoint position;  
  43.   
  44. //層在父層上的位置的Z軸的分量,默認值零zero,支持動畫  
  45. @property CGFloat zPosition;  
  46.   
  47. //限定層邊界的錨點,就像在歸一化的層的點座標,'(0,0)'是邊界矩形的左下角'(1,1)'是右上角。默認爲'(0.5,0.5)“,即邊界矩形的中心。支持動畫。  
  48. @property CGPoint anchorPoint;  
  49.   
  50. //層的錨點的Z分量(參考點位置和變換),默認爲零。支持動畫。  
  51. @property CGFloat anchorPointZ;  
  52.   
  53. //3D變換,用於層邊界相對於錨點的變換。默認爲恆等變換。支持動畫。  
  54. @property CATransform3D transform;  
  55.   
  56. //用來訪問'變換'屬性:仿射變換的存取器方法。  
  57. - (CGAffineTransform)affineTransform;  
  58. - (void)setAffineTransform:(CGAffineTransform)m;  
  59.   
  60. //與View的frame屬性不同,在層次結構中每一層都有一個隱含的幀長方形, `position', `bounds', `anchorPoint',and `transform'屬性改變時,它也會發生相應的變化  
  61. @property CGRect frame;  
  62.   
  63. //當爲YES時不顯示層與其子層,默認是NO,支持動畫  
  64. @property(getter=isHidden) BOOL hidden;  
  65.   
  66. //當時false時,層遠離觀察者的那一面隱藏(圖層有雙面,是否都顯示,設置NO意思背面看不到,當爲NO時,然後旋轉180度,則看不到layer層),默認是YES,支持動畫。  
  67. @property(getter=isDoubleSided) BOOL doubleSided;   
  68.   
  69. //表示層(及其子層)的幾何是否被垂直旋轉,默認NO。該屬性可以改變默認圖層y座標的方向。當翻轉變換被調用時,使用該屬性來調整圖層的方向有的時候是必需的。如果父視圖使用了翻轉變換,它的子視圖內容(以及它對應的圖層)將經常被顛倒。在這種情況下,設置子圖層的geometryFlipped屬性爲YES是一種修正該問題最簡單的方法。在OS X 10.8及以上版本,AppKit負責管理該屬性,你不應該更改它。對於iOS app,不推薦使用geometryFlipped屬性。  
  70. //是否進行y軸的方向翻轉  
  71. @property(getter=isGeometryFlipped) BOOL geometryFlipped;  
  72.   
  73. //獲取當前layer內容y軸方向是否被翻轉了  
  74. - (BOOL)contentsAreFlipped;  
  75.   
  76. //父層  
  77. @property(nullable, readonlyCALayer *superlayer;  
  78.   
  79. //從其父layer層上移除  
  80. - (void)removeFromSuperlayer;  
  81.   
  82. //所有子layer數組  
  83. @property(nullable, copy) NSArray<CALayer *> *sublayers;  
  84.   
  85. //添加一個子layer  
  86. - (void)addSublayer:(CALayer *)layer;  
  87.   
  88. //插入一個子layer  
  89. - (void)insertSublayer:(CALayer *)layer atIndex:(unsigned)idx;  
  90. - (void)insertSublayer:(CALayer *)layer below:(nullable CALayer *)sibling;  
  91. - (void)insertSublayer:(CALayer *)layer above:(nullable CALayer *)sibling;  
  92.   
  93. //替換一個子layer  
  94. - (void)replaceSublayer:(CALayer *)layer with:(CALayer *)layer2;  
  95.   
  96. //對其子layer進行3D變換  
  97. @property CATransform3D sublayerTransform;  
  98.   
  99. //遮罩層layer  
  100. @property(nullable, strongCALayer *mask;  
  101.   
  102. //是否進行bounds的切割,在設置圓角屬性時會設置爲YES  
  103. @property BOOL masksToBounds;  
  104.   
  105. //下面這些方法用於座標轉換  
  106. - (CGPoint)convertPoint:(CGPoint)p fromLayer:(nullable CALayer *)l;  
  107. - (CGPoint)convertPoint:(CGPoint)p toLayer:(nullable CALayer *)l;  
  108. - (CGRect)convertRect:(CGRect)r fromLayer:(nullable CALayer *)l;  
  109. - (CGRect)convertRect:(CGRect)r toLayer:(nullable CALayer *)l;  
  110.   
  111. //  
  112. - (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(nullable CALayer *)l;  
  113. - (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(nullable CALayer *)l;  
  114.   
  115.   
  116. //===================命中檢測方法  
  117. //iOS中,hit-Testing的作用就是找出這個觸摸點下面的View(layer)是什麼,HitTest會檢測這個點擊的點是不是發生在這個View(layer)上  
  118. //返回包含某一點的最上層的子layer  
  119. - (nullable CALayer *)hitTest:(CGPoint)p;  
  120.   
  121. //返回layer是否包含某一點  
  122. - (BOOL)containsPoint:(CGPoint)p;  
  123.   
  124.   
  125. //===================layer內容屬性和方法  
  126. //設置layer的內容,一般會設置爲CGImage的對象  
  127. @property(nullable, strongid contents;  
  128.   
  129. //獲取內容的rect尺寸  
  130. @property CGRect contentsRect;  
  131.   
  132. /*contentsGravity屬性決定了內容對齊與填充方式,它可以分爲兩個方面: 
  133.  1.不改變內容的原始大小 
  134.  這種模式中不會改變內容的原始大小,如果層的尺寸小於內容的尺寸,則內容會被切割,如果層的尺寸大於內容的尺寸,多出的部分將會顯示層的背景顏色。 
  135.  2.改變內容的尺寸大小 
  136.  這種模式設置的實際上是一種填充方式: 
  137. */  
  138. @property(copyNSString *contentsGravity;  
  139.   
  140. //設置內容的縮放  
  141. @property CGFloat contentsScale  
  142.   
  143. //這個屬性確定一個矩形區域,當內容進行拉伸或者縮放的時候,這一部分的區域是會被形變的,例如默認設置爲(0,0,1,1),則整個內容區域都會參與形變。如果我們設置爲(0.25,0.25,0.5,0.5),那麼只有中間0.5*0.5比例寬高的區域會被拉伸,四周都不會。  
  144. @property CGRect contentsCenter;  
  145.   
  146. //設置縮小的模式  
  147. @property(copyNSString *minificationFilter;  
  148.   
  149. //設置放大的模式  
  150. @property(copyNSString *magnificationFilter;  
  151.   
  152. //縮放因子  
  153. @property float minificationFilterBias;  
  154.   
  155. //設置內容是否完全不透明。默認是NO  
  156. @property(getter=isOpaque) BOOL opaque;  
  157.   
  158. //重新加載繪製內容  
  159. - (void)display;  
  160.   
  161. //設置內容爲需要重新繪製  
  162. - (void)setNeedsDisplay;  
  163. //設置某一區域內容需要重新繪製  
  164. - (void)setNeedsDisplayInRect:(CGRect)r;  
  165.   
  166. //獲取是否需要重新繪製  
  167. - (BOOL)needsDisplay;  
  168.   
  169. //如果需要,進行內容重繪  
  170. - (void)displayIfNeeded;  
  171.   
  172. //這個屬性設置爲YES,當內容改變時會自動調用- (void)setNeedsDisplay函數.默認是NO  
  173. @property BOOL needsDisplayOnBoundsChange;  
  174.   
  175. //默認是NO  
  176. @property BOOL drawsAsynchronously  
  177.   
  178. //繪製與讀取內容  
  179. - (void)drawInContext:(CGContextRef)ctx;  
  180. - (void)renderInContext:(CGContextRef)ctx;  
  181.   
  182. //這個屬性值用於限定層的邊緣是如何柵格化。通常,該屬性用於關閉抗鋸齒用於邊沿的其他緊靠層的邊緣,以消除否則會發生的接縫。默認值時所有值都抗鋸齒。  
  183. @property CAEdgeAntialiasingMask edgeAntialiasingMask;  
  184.   
  185. //當爲真時,則層對由edgeAntialiasingMask屬性的值要求的邊抗鋸齒。默認值是從主束的Info.plist布爾UIViewEdgeAntialiasing屬性讀取。如果Info.plist中沒有找到值則,默認值是NO。  
  186. @property BOOL allowsEdgeAntialiasing;  
  187.   
  188. //設置背景顏色 默認nil.  
  189. @property(nullable) CGColorRef backgroundColor;  
  190.   
  191. //設置圓角半徑 默認zero  
  192. @property CGFloat cornerRadius;  
  193.   
  194. //設置邊框寬度  
  195. @property CGFloat borderWidth;  
  196.   
  197. //設置邊框顏色  
  198. @property(nullable) CGColorRef borderColor;  
  199.   
  200. //設置透明度  
  201. @property float opacity;  
  202.   
  203. //(待續。。。)  
  204. @property BOOL allowsGroupOpacity;  
  205.   
  206. @property(nullable, strongid compositingFilter;  
  207.   
  208. @property(nullable, copyNSArray *filters;  
  209.   
  210. @property(nullable, copyNSArray *backgroundFilters;  
  211.   
  212. @property BOOL shouldRasterize;  
  213.   
  214. @property CGFloat rasterizationScale;  
  215.   
  216. //===================layer的陰影屬性  
  217. //設置陰影顏色  
  218. @property(nullable) CGColorRef shadowColor;  
  219.   
  220. //設置陰影透明度,默認0,值在[0,1]之間,支持動畫  
  221. @property float shadowOpacity;  
  222.   
  223. //設置陰影偏移量. 默認(0, -3),支持動畫.  
  224. @property CGSize shadowOffset;  
  225.   
  226. //設置陰影圓角半徑  
  227. @property CGFloat shadowRadius;  
  228.   
  229. //設置陰影路徑.默認null,支持動畫.  
  230. @property(nullable) CGPathRef shadowPath;  
  231.   
  232. //===================佈局方法  
  233. - (CGSize)preferredFrameSize;  
  234.   
  235. - (void)setNeedsLayout;  
  236.   
  237. - (BOOL)needsLayout;  
  238.   
  239. - (void)layoutIfNeeded;  
  240.   
  241. - (void)layoutSublayers;  
  242.   
  243. //===================行爲方法  
  244. + (nullable id<CAAction>)defaultActionForKey:(NSString *)event;  
  245.   
  246. - (nullable id<CAAction>)actionForKey:(NSString *)event;  
  247.   
  248. @property(nullable, copy) NSDictionary<NSString *, id<CAAction>> *actions;  
  249.   
  250. //===================layer的關於動畫的方法  
  251.   
  252. //添加一個動畫對象 key值起到id的作用,通過key值,可以取到這個動畫對象  
  253. - (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key;  
  254.   
  255. //移除所有動畫對象  
  256. - (void)removeAllAnimations;  
  257.   
  258. //移除某個動畫對象  
  259. - (void)removeAnimationForKey:(NSString *)key;  
  260.   
  261. //獲取所有動畫對象的key值  
  262. - (nullable NSArray<NSString *> *)animationKeys;  
  263.   
  264. //通過key值獲取動畫對象  
  265. - (nullable CAAnimation *)animationForKey:(NSString *)key;  
  266.   
  267.   
  268. //===================layer的其他屬性  
  269.   
  270. //layer的名字,用於層的管理,默認nil  
  271. @property(nullable, copyNSString *name;  
  272.   
  273. //代理,默認nil  
  274. @property(nullable, weak) id delegate;  
  275. //風格屬性字典  
  276. @property(nullable, copyNSDictionary *style;  
  277. @end  
  278.   
  279. //=====================CAAction協議  
  280. @protocol CAAction  
  281. /* 
  282. CAAction協議定義了行爲對象如何被調用。實現CAAction協議的類包含一個方法runActionForKey:object:arguments:。當行爲對象收到一個 
  283. runActionForKey:object:arguments:的消息時,行爲標識符、行爲發生所在的圖層、額外的參數字典會被作爲參數傳遞給方法。通常行爲對象是CAAnimation的 
  284. 子類實例,它實現了CAAction協議。然而你也可以返回任何實現了CAAction協議的類對象。當實例收到runActionForKey:object:arguments:的消息時,它需要執 
  285. 行相應的行爲。 
  286. */  
  287. - (void)runActionForKey:(NSString *)event object:(id)anObject  
  288.               arguments:(nullable NSDictionary *)dict;  
  289. @end  
  290.   
  291. // NSNull protocol conformance.  (待續。。。)  
  292. @interface NSNull (CAActionAdditions) <CAAction>  
  293.   
  294. @end  
  295.   
  296. //==================NSObject的類別  
  297. //繪製  
  298. @interface NSObject (CALayerDelegate)  
  299.   
  300. - (void)displayLayer:(CALayer *)layer;  
  301.   
  302. - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;  
  303.   
  304. - (void)layoutSublayersOfLayer:(CALayer *)layer;  
  305.   
  306. - (nullable id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event;  
  307. @end  
  308. //  
  309. //********************** Layer的contentsGravity 屬性值******************************/  
  310. //1.不改變內容的原始大小.下面的這些設置方式爲這種模式:  
  311. CA_EXTERN NSString * const kCAGravityCenter  
  312. __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);  
  313. CA_EXTERN NSString * const kCAGravityTop  
  314. __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);  
  315. CA_EXTERN NSString * const kCAGravityBottom  
  316. __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);  
  317. CA_EXTERN NSString * const kCAGravityLeft  
  318. __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);  
  319. CA_EXTERN NSString * const kCAGravityRight  
  320. __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);  
  321. CA_EXTERN NSString * const kCAGravityTopLeft  
  322. __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);  
  323. CA_EXTERN NSString * const kCAGravityTopRight  
  324. __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);  
  325. CA_EXTERN NSString * const kCAGravityBottomLeft  
  326. __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);  
  327. CA_EXTERN NSString * const kCAGravityBottomRight  
  328. __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);  
  329. //改變內容的尺寸大小.這種模式設置的實際上是一種填充方式參數如下:  
  330. CA_EXTERN NSString * const kCAGravityResize  
  331. __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);  
  332. CA_EXTERN NSString * const kCAGravityResizeAspect  
  333. __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);  
  334. CA_EXTERN NSString * const kCAGravityResizeAspectFill  
  335. __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);  
  336.   
  337. //********************** Layer的Contents filter names.模式參數如下**/  
  338. //臨近插值  
  339. CA_EXTERN NSString * const kCAFilterNearest  
  340. __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);  
  341. //線性拉伸  
  342. CA_EXTERN NSString * const kCAFilterLinear  
  343. __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);  
  344. //瓦片複製拉伸  
  345. CA_EXTERN NSString * const kCAFilterTrilinear  
  346. __OSX_AVAILABLE_STARTING (__MAC_10_6, __IPHONE_3_0);  
  347.   
  348. /** Layer event names. **/  
  349. CA_EXTERN NSString * const kCAOnOrderIn  
  350. __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);  
  351. CA_EXTERN NSString * const kCAOnOrderOut  
  352. __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);  
  353.   
  354. /** The animation key used for transitions. **/  
  355.   
  356. CA_EXTERN NSString * const kCATransition  
  357. __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);  
  358.   
  359. NS_ASSUME_NONNULL_END  
  360.   
  361. @end  

四、CALayer的簡單使用

1.CALayer是被定義在QuartzCore框架中的,因此要想使用CALayer,先導入QuartzCore框架(如果已經導入了UIKit框架,就無需導入QuartzCore框架,因爲UIKit本身已經引入QuartzCore框架 )
1> 點擊項目名稱,然後點擊右邊TARGETS下面的target



2> 點擊Build Pases後,展開Link Binary....,添加 + 號

3> 在搜索框中輸入"Quartz",選中QuartzCore.framework,最後add添加

4> 添加完畢後,這個框架就會出現在項目文件夾中

如果你覺得位置不好看,還可以將它拖到Frameworks文件夾下,跟其他框架放一起

2.在項目代碼中導入QuartzCore框架的主頭文件
[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. #import <QuartzCore/QuartzCore.h>  
3.通過CALayer修改UIImageView的界面屬性
你也可以使用UIButton或者UILabel,這裏就以UIImageView爲例子
1> 先創建一個UIImageView,添加到控制器的view中   
[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. UIImage *image = [UIImage imageNamed:@"lufy.png"];  
  2. UIImageView *imageView = [[[UIImageView alloc] initWithImage:image] autorelease];  
  3. imageView.center = CGPointMake(100100);  
  4. [self.view addSubview:imageView];  


2> 設置陰影
[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. imageView.layer.shadowColor = [UIColor grayColor].CGColor;  
  2. imageView.layer.shadowOffset = CGSizeMake(1010);  
  3. imageView.layer.shadowOpacity = 0.5;<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"  
* 第1行設置陰影的顏色爲灰色,注意,這裏使用的是UIColor的CGColor屬性,是一種CGColorRef類型的數據

* 第2行設置陰影的偏移大小,可以看出陰影往原圖的右下角偏移

* 第3行設置陰影的不透明度爲0.5,表示半透明。如果爲1,代表完全不透明。


3> 設置圓角大小

通過layer屬性可以訪問視圖內部的CALayer對象

[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. imageView.layer.cornerRadius = 10;  
  2. imageView.layer.masksToBounds = YES;  

* 第1行設置圓角半徑爲10

* 第2行的maskToBounds=YES:可以看做是強制內部的所有子層支持圓角效果,少了這個設置,UIImageView是不會有圓角效果的

* 注意,如果設置了maskToBounds=YES,那將不會有陰影效果


4> 設置邊框寬度和顏色
[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. imageView.layer.borderWidth = 5;  
  2. imageView.layer.borderColor = [UIColor redColor].CGColor;  
* 第1行設置邊框寬度爲5

* 第2行設置邊框顏色爲紅色


5> 設置旋轉
[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. imageView.layer.transform = CATransform3DMakeRotation(M_PI_4001);  
* 利用transform屬性可以設置旋轉、縮放等效果
* M_PI_4表示四分之π,順時針旋轉45°
* 後面的(0, 0, 1)表示Z軸這個向量,修改這個向量可以做一些三維旋轉效果,你可以隨便改個值試一下,比如(1, 1, 1)

* 總體的意思是layer會繞着Z軸順時針旋轉45°,也就是在x、y平面進行旋轉


第二部分:創建新的層

一、添加一個簡單的圖層

[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1.  CALayer *myLayer = [CALayer layer];  
  2.  //設置層的寬度和高度(100x100)  
  3.  myLayer.bounds = CGRectMake(00100100);  
  4.  //設置層的位置  
  5.  myLayer.position = CGPointMake(100100);  
  6.  //設置層的背景顏色:紅色  
  7.  myLayer.backgroundColor = [UIColor redColor].CGColor;  
  8.  //設置層的圓角半徑爲10  
  9.  myLayer.cornerRadius = 10;  
  10.  //添加myLayer到控制器的view的layer中  
  11. [self.view.layer addSublayer:myLayer];  

* 第1行創建了一個自動釋放的CALayer對象,你也可以使用經典的alloc和init方法來創建
* 第12行將創建好的層添加到控制器的view的層中


二、添加一個顯示圖片的圖層

[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. CALayer *myLayer = [CALayer layer];  
  2. / 設置層的寬度和高度(100x100)  
  3. myLayer.bounds = CGRectMake(00100100);  
  4. / 設置層的位置  
  5. myLayer.position = CGPointMake(100100);  
  6. / 設置需要顯示的圖片  
  7. myLayer.contents = (id)[UIImage imageNamed:@"lufy.png"].CGImage;  
  8. / 設置層的圓角半徑爲10  
  9. myLayer.cornerRadius = 10;  
  10. / 如果設置了圖片,需要設置這個屬性爲YES纔有圓角效果  
  11. myLayer.masksToBounds = YES;   
  12. / 添加myLayer到控制器的view的layer中  
  13. [self.view.layer addSublayer:myLayer];  
* 在第7行設置需要顯示的圖片,注意,這裏用的是UIImage的CGImage屬性,是一種CGImageRef類型的數據

三、爲什麼CALayer中使用CGColorRef和CGImageRef這2種數據類型,而不用UIColor和UIImage?

* 首先要知道:CALayer是定義在QuartzCore框架中的;CGImageRef、CGColorRef兩種數據類型是定義在CoreGraphics框架中的;UIColor、UIImage是定義在UIKit框架中的
* 其次,QuartzCore框架和CoreGraphics框架是可以跨平臺使用的,在iOS和Mac OS X上都能使用,但是UIKit只能在iOS中使用
* 因此,爲了保證可移植性,QuartzCore不能使用UIImage、UIColor,只能使用CGImageRef、CGColorRef
* 不過很多情況下,可以通過UIKit對象的特定方法,得到CoreGraphics對象,比如UIImage的CGImage方法可以返回一個CGImageRef

四、UIView和CALayer的選擇

細心的朋友不難發現,其實前面的2個效果不僅可以通過添加層來實現,還可以通過添加UIView來實現。比如,第1個紅色的層可以用一個UIView來實現,第2個顯示圖片的層可以用一個UIImageView來實現。 既然CALayer和UIView都能實現相同的顯示效果,那究竟該選擇誰好呢?

* 其實,對比CALayer,UIView多了一個事件處理的功能。也就是說,CALayer不能處理用戶的觸摸事件,而UIView可以
* 所以,如果顯示出來的東西需要跟用戶進行交互的話,用UIView;如果不需要跟用戶進行交互,用UIView或者CALayer都可以
* 當然,CALayer的性能會高一些,因爲它少了事件處理的功能,更加輕量級

五、UIView和CALayer的其他關係

* UIView可以通過subviews屬性訪問所有的子視圖,類似地,CALayer也可以通過sublayers屬性訪問所有的子層
* UIView可以通過superview屬性訪問父視圖,類似地,CALayer也可以通過superlayer屬性訪問父層
* 下面再看一張UIView和CALayer的關係圖:


如果兩個UIView是父子關係,那麼它們內部的CALayer也是父子關係。

第三部分層的屬性

一、隱式動畫屬性

* 在前面幾講中已經提到,每一個UIView內部都默認關聯着一個CALayer,我們可用稱這個Layer爲Root Layer(根層)。所有的非Root Layer,也就是手動創建的CALayer對象,都存在着隱式動畫。

* 與UIView不同,CALayer實際上包含了一個表現層和一個模型層。模型層是用來在內存中存儲必要的圖層信息的。表現層則是用在將圖層顯示在屏幕上,併爲此做了相應的優化。

如果說一個動畫是隱式動畫,那就意味着用做動畫的屬性是在模型層中被修改的然後在通過表現層傳遞出來,並最終顯示在屏幕上。 如果動畫是顯示動畫,進行動畫的屬性就是隻存在於表現層的,而原始的模型層(在進行動畫之前的)會保持不變。這就意味着除非做其他的動作,否則在一個顯示動畫結束之後,CALayer會回到動畫開始之前的狀態,因爲下面的模型層並沒有被修改.

* 當對非Root Layer的部分屬性進行相應的修改時,默認會自動產生一些動畫效果,這些屬性稱爲Animatable Properties(可動畫屬性)。
* 列舉幾個常見的Animatable Properties:

bounds:用於設置CALayer的寬度和高度。修改這個屬性會產生縮放動畫
backgroundColor:用於設置CALayer的背景色。修改這個屬性會產生背景色的漸變動畫
position:用於設置CALayer的位置。修改這個屬性會產生平移動畫
比如:假設一開始CALayer的position爲(100, 100),然後在某個時刻修改爲(200, 200),那麼整個CALayer就會在短時間內從(100, 100)這個位置平移到(200, 200)
* 我們也可以從官方文檔中查詢所有的Animatable Properties

1.點擊Window -> Organizer

2.在搜索框輸入"animatable"即可

二、position和anchorPoint

* position和anchorPoint屬性都是CGPoint類型的

* position可以用來設置CALayer在父層中的位置,它是以父層的左上角爲座標原點(0, 0)

* anchorPoint稱爲"定位點",它決定着CALayer身上的哪個點會在position屬性所指的位置。它的x、y取值範圍都是0~1,默認值爲(0.5, 0.5)
1.創建一個CALayer,添加到控制器的view的layer中

[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1.  CALayer *myLayer = [CALayer layer];  
  2.   // 設置層的寬度和高度(100x100)  
  3.  myLayer.bounds = CGRectMake(00100100);  
  4.  // 設置層的位置  
  5.  myLayer.position = CGPointMake(100100);  
  6.  // 設置層的背景顏色:紅色  
  7.  myLayer.backgroundColor = [UIColor redColor].CGColor;   
  8.  // 添加myLayer到控制器的view的layer中  
  9. [self.view.layer addSublayer:myLayer];  
第5行設置了myLayer的position爲(100, 100),又因爲anchorPoint默認是(0.5, 0.5),所以最後的效果是:myLayer的中點會在父層的(100, 100)位置

注意,藍色線是我自己加上去的,方便大家理解,並不是默認的顯示效果。兩條藍色線的寬度均爲100。
2.若將anchorPoint改爲(0, 0),myLayer的左上角會在(100, 100)位置

[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. myLayer.anchorPoint = CGPointMake(00);  

3.若將anchorPoint改爲(1, 1),myLayer的右下角會在(100, 100)位置
[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. myLayer.anchorPoint = CGPointMake(11);  
 
4.將anchorPoint改爲(0, 1),myLayer的左下角會在(100, 100)位置
[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. myLayer.anchorPoint = CGPointMake(01);  


我想,你應該已經明白anchorPoint的用途了吧,它決定着CALayer身上的哪個點會在position所指定的位置上。它的x、y取值範圍都是0~1,默認值爲(0.5, 0.5),因此,默認情況下,CALayer的中點會在position所指定的位置上。當anchorPoint爲其他值時,以此類推。

第四部分:自定義圖層

方法描述:創建一個CALayer的子類,然後覆蓋drawInContext:方法,使用Quartz2D API進行繪圖
創建一個CALayer的子類



2.在.m文件中覆蓋drawInContext:方法,在裏面繪圖
[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. @implementation MJLayer    
  2.  #pragma mark 繪製一個實心三角形  
  3.  - (void)drawInContext:(CGContextRef)ctx {  
  4.     // 設置爲藍色  
  5.     CGContextSetRGBFillColor(ctx, 0011);      
  6.     // 設置起點  
  7.    CGContextMoveToPoint(ctx, 500);  
  8.     // 從(50, 0)連線到(0, 100)  
  9.    CGContextAddLineToPoint(ctx, 0100);  
  10.     // 從(0, 100)連線到(100, 100)  
  11.     CGContextAddLineToPoint(ctx, 100100);  
  12.     // 合併路徑,連接起點和終點  
  13.     CGContextClosePath(ctx);       
  14.     // 繪製路徑  
  15.     CGContextFillPath(ctx);  
  16.   }  
  17.  @end  

3.在控制器中添加圖層到屏幕上

[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1.   MJLayer *layer = [MJLayer layer];  
  2.    //設置層的寬高  
  3.   layer.bounds = CGRectMake(00100100);  
  4.    //設置層的位置  
  5.   layer.position = CGPointMake(100100);  
  6.    // 開始繪製圖層  
  7.   [layer setNeedsDisplay];  
  8.  [self.view.layer addSublayer:layer];  

注意第7行,需要調用setNeedsDisplay這個方法,纔會觸發drawInContext:方法的調用,然後進行繪圖

二、自定義層的方法2

方法描述:設置CALayer的delegate,然後讓delegate實現drawLayer:inContext:方法,當CALayer需要繪圖時,會調用delegate的drawLayer:inContext:方法進行繪圖。
* 這裏要注意的是:不能再將某個UIView設置爲CALayer的delegate,因爲UIView對象已經是它內部根層的delegate,再次設置爲其他層的delegate就會出問題。UIView和它內部CALayer的默認關係圖:


1.創建新的層,設置delegate,然後添加到控制器的view的layer中

[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. CALayer *layer = [CALayer layer];  
  2. // 設置delegate  
  3.  layer.delegate = self;  
  4. // 設置層的寬高  
  5.  layer.bounds = CGRectMake(00100100);  
  6. // 設置層的位置  
  7.  layer.position = CGPointMake(100100);  
  8. // 開始繪製圖層  
  9.  [layer setNeedsDisplay];  
  10.  [self.view.layer addSublayer:layer];  

* 在第3行設置了CALayer的delegate,這裏的self是指控制器
* 注意第9行,需要調用setNeedsDisplay這個方法,纔會通知delegate進行繪圖
2.讓CALayer的delegate(前面設置的是控制器)實現drawLayer:inContext:方法

[objc] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. #pragma mark 畫一個矩形框  
  2. - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {  
  3.     // 設置藍色  
  4.     CGContextSetRGBStrokeColor(ctx, 0011);  
  5.      // 設置邊框寬度  
  6.     CGContextSetLineWidth(ctx, 10);       
  7.     // 添加一個跟層一樣大的矩形到路徑中  
  8.     CGContextAddRect(ctx, layer.bounds);       
  9.     // 繪製路徑  
  10.     CGContextStrokePath(ctx);  
  11.  }   
 

 三、其他

1.總結
無論採取哪種方法來自定義層,都必須調用CALayer的setNeedsDisplay方法才能正常繪圖。
2.UIView的詳細顯示過程
* 當UIView需要顯示時,它內部的層會準備好一個CGContextRef(圖形上下文),然後調用delegate(這裏就是UIView)的drawLayer:inContext:方法,並且傳入已經準備好的CGContextRef對象。而UIView在drawLayer:inContext:方法中又會調用自己的drawRect:方法
* 平時在drawRect:中通過UIGraphicsGetCurrentContext()獲取的就是由層傳入的CGContextRef對象,在drawRect:中完成的所有繪圖都會填入層的CGContextRef中,然後被拷貝至屏幕

參考:

CALayer的簡單介紹

CALayer的使用

更多動畫知識:

iOS Core Animation: Advanced Techniques中文譯本

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章