如圖:
UIView
可以處理觸摸事件,但CALayer
是不支持交互的(不清楚具體的相應鏈的)。實際上這些背后關聯的圖層才是真正用來在屏幕上顯示和做動畫,UIView
僅僅是對它的一個封裝,提供了一些iOS類似于處理觸摸的具體功能,以及Core Animation底層方法的高級接口。
<四個層級關系>:視圖層級、圖層樹、呈現樹和渲染樹。
屬性介紹
-
Contents:
事實上,你真正要賦值的類型應該是CGImageRef,它是一個指向CGImage結構的指針。UIImage有一個CGImage屬性,它返回一個"CGImageRef",如果你想把這個值直接賦值給CALayer的
contents
,那你將會得到一個編譯錯誤。因為CGImageRef并不是一個真正的Cocoa對象,而是一個Core Foundation類型??梢酝ㄟ^bridged關鍵字轉換。
@property(nullable, strong) id contents;
-
contentMode:
//對應UIView的contentMode.
@property(copy) NSString *contentsGravity;
-
geometryFlipped:
//決定了一個圖層的坐標是否相對于父圖層垂直翻轉。是為了適配iOS和OS X兩種不同坐標系的情況。
@property(getter=isGeometryFlipped) BOOL geometryFlipped;
-
zPosition:
//常用于做CATransform3D變換及更改圖層的顯示順序。<不能改變事件傳遞的順序>
@property CGFloat zPosition;
-
anchorPointZ:
//在Z軸上描述圖層位置的浮點類型
@property CGFloat anchorPointZ;
-
position:
指定了anchorPoint相對于父圖層的位置。
//對應UIView的center.
@property CGPoint position;
-
anchorPoint:
錨點,可以理解為固定圖層的點。相對于自身坐標系,取值范圍0~1,默認(0.5,0.5)。
@property CGPoint anchorPoint;
position和anchorPoint的換算公式:徹底理解position和anchorPoint
position.x = frame.origin.x + anchorPoint.x * bounds.size.width;
position.y = frame.origin.y + anchorPoint.y * bounds.size.height;
-
contentsScale:
屬性定義了寄宿圖的像素尺寸和視圖大小的比例,默認情況下它是一個值為1.0的浮點數。當設置了
contentsGravity
屬性會有所影響。
//如果contentsScale設置為1.0,將會以每個點1個像素繪制圖片。并且把contentsGravity設置為kCAGravityCenter(這個值并不會拉伸圖片),那將會有很明顯的變化。
@property CGFloat contentsScale
-
contentsRect:
屬性允許我們在圖層邊框里顯示寄宿圖的一個子域,
contentsRect
不是按點來計算的,它使用了單位坐標,單位坐標指定在0到1之間,是一個相對值(像素和點就是絕對值)
@property CGRect contentsRect;
-
contentsCenter:《contentsCenter 介紹》
其實是一個CGRect,它定義了一個固定的邊框和一個在圖層上可拉伸的區域。
//單位坐標,定義的區域會被全面拉伸(也就是從四個方向進行放大或者縮小),所'侵占'的地方的視圖也會進行相應的拉伸變換。
@property CGRect contentsCenter;
-
masksToBounds:
//等同于UIView的clipsToBounds.
@property BOOL masksToBounds;
-
mask:
是個CALayer類型,有和其他圖層一樣的繪制和布局屬性。它類似于一個子圖層,相對于父圖層(即擁有該屬性的圖層)布局,但是它卻不是一個普通的子圖層。不同于那些繪制在父圖層中的子圖層,
mask
圖層定義了父圖層的部分可見區域。
@property(nullable, strong) CALayer *mask;
-
minificationFilter && magnificationFilter
@property(copy) NSString *minificationFilter;//縮?。?@property(copy) NSString *magnificationFilter;//放大:
--值的介紹:
1。kCAFilterLinear
:默認值,采用雙線性濾波算法,它在大多數情況下都表現良好。雙線性濾波算法通過對多個像素取樣最終生成新的值,得到一個平滑的表現不錯的拉伸。但是當放大倍數比較大的時候圖片就模糊不清了。
2。kCAFilterTrilinear
:三線性濾波算法存儲了多個大小情況下的圖片(也叫多重貼圖),并三維取樣,同時結合大圖和小圖的存儲進而得到最后的結果。這不僅提高了性能,也避免了小概率因舍入錯誤引起的取樣失靈的問題.
3。kCAFilterNearest
:取樣最近的單像素點而不管其他的顏色。這樣做非常快,也不會使圖片模糊。但是,最明顯的效果就是,會使得壓縮圖片更糟,圖片放大之后也顯得塊狀或是馬賽克嚴重。(不推薦)。
--總結:
對于比較小的圖或者是差異特別明顯,極少斜線的大圖,最近過濾算法會保留這種差異明顯的特質以呈現更好的結果。但是對于大多數的圖尤其是有很多斜線或是曲線輪廓的圖片來說,最近過濾算法會導致更差的結果。換句話說,線性過濾保留了形狀,最近過濾則保留了像素的差異。
-
shouldRasterize:
為了啟用
shouldRasterize
屬性,我們設置了圖層的rasterizationScale
屬性。默認情況下,所有圖層拉伸都是1.0, 所以如果你使用了shouldRasterize
屬性,你就要確保你設置了rasterizationScale
屬性去匹配屏幕,以防止出現Retina屏幕像素化的問題。(info.plist文件中有個全局設置屬性:UIViewGroupOpacity)
//組透明效果
@property BOOL shouldRasterize;
-
affineTransform
這里只提供了set和get方法。
- (CGAffineTransform)affineTransform;
- (void)setAffineTransform:(CGAffineTransform)m;
—栗子:
CGAffineTransform affine = CGAffineTransformMakeRotation(M_PI_4);
blueLayer.affineTransform = affine;
代理
CALayer有一個可選的
delegate
屬性,實現了CALayerDelegate
協議,當CALayer需要一個內容特定的信息時,就會從協議中請求。CALayerDelegate是一個非正式協議,其實就是說沒有CALayerDelegate @protocol可以讓你在類里面引用啦。你只需要調用你想調用的方法,CALayer會幫你做剩下的。(delegate
屬性被聲明為id類型,所有的代理方法都是可選的)。
- 當需要被重繪時,CALayer會請求它的代理給他一個寄宿圖來顯示。它通過調用下面這個方法做到的:
- (void)displayLayer:(CALayerCALayer *)layer;
- 如果代理不實現
-displayLayer:
方法,CALayer就會轉而嘗試調用下面這個方法,在調用這個方法之前,CALayer創建了一個合適尺寸的空寄宿圖(尺寸由bounds
和contentsScale
決定)和一個Core Graphics的繪制上下文環境,為繪制寄宿圖做準備,他作為ctx參數傳入。
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
—栗子:
CALayer * delegateLayer = [CALayer layer];
[self.view.layer addSublayer:delegateLayer];
delegateLayer.frame = CGRectMake(50, 100, 300, 400);
delegateLayer.backgroundColor = [UIColor brownColor].CGColor;
delegateLayer.delegate = self;//這里需遵循CALayerDelegate協議。
[delegateLayer display];//這里需要顯式地調用了-display。不同于UIView,當圖層顯示在屏幕上時,CALayer不會自動重繪它的內容。它把重繪的決定權交給了開發者。
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
//draw a thick red circle
CGContextSetLineWidth(ctx, 10.0f);
CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextStrokeEllipseInRect(ctx, layer.bounds);
}
—效果:
—注意:
盡管我們沒有用masksToBounds
屬性,繪制的那個圓仍然沿邊界被裁剪了。這是因為當你使用CALayerDelegate繪制寄宿圖的時候,并沒有對超出邊界外的內容提供繪制支持。
當UIView創建了它的宿主圖層時,它就會自動地把圖層的delegate設置為它自己,并提供了一個-displayLayer:
的實現,那所有的問題就都沒了。
當使用寄宿了視圖的圖層的時候,你也不必實現-displayLayer:
和-drawLayer:inContext:
方法來繪制你的寄宿圖。通常做法是實現UIView的-drawRect:
方法,UIView就會幫你做完剩下的工作,包括在需要重繪的時候調用-display
方法。
其它方法
- UIView會在初始化的時候調用+layerClass方法,然后用它的返回類型來創建宿主圖層。(特別適用,返回的值代表self.layer)
+(Class)layerClass
- 以下方法可以把定義在一個圖層坐標系下的點或者矩形轉換成另一個圖層坐標系下的點或者矩形.
- (CGPoint)convertPoint:(CGPoint)point fromLayer:(CALayer *)layer;//得到layer上的point的點相對于方法調用者的相對point。
- (CGPoint)convertPoint:(CGPoint)point toLayer:(CALayer *)layer;
- (CGRect)convertRect:(CGRect)rect fromLayer:(CALayer *)layer;
- (CGRect)convertRect:(CGRect)rect toLayer:(CALayer *)layer;
- 接受一個在本圖層坐標系下的
CGPoint
,如果這個點在圖層frame
范圍內就返回YES
。需要把觸摸坐標轉換成每個圖層坐標系下的坐標,結果很不方便。
- (BOOL)containsPoint:(CGPoint)p;
- 方法同樣接受一個
CGPoint
類型參數,而不是BOOL
類型,它返回圖層本身,或者包含這個坐標點的葉子節點圖層。這意味著不再需要像使用-containsPoint:
那樣,人工地在每個子圖層變換或者測試點擊的坐標。如果這個點在最外面圖層的范圍之外,則返回nil。
- (nullable CALayer *)hitTest:(CGPoint)p;
—栗子:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint point = [[touches anyObject] locationInView:self.view];
CALayer *layer = [self.layerView.layer hitTest:point];
if (layer == self.blueLayer) {
} else if (layer == self.layerView.layer) {
}
}