文章分享至個人我的博客:https://cainluo.github.io/14773198375857.htmlhtml
前一章咱們瞭解真正了CALayer是幹嗎的, 《Core Animation》初識CALayer今天咱們就來說講CALayer Geometry. 最後: 若是你有更好的建議或者對這篇文章有不滿的地方, 請聯繫我, 我會參考大家的意見再進行修改, 聯繫我時, 請備註
Core Animation
若是以爲好的話, 但願你們也能夠打賞一下~嘻嘻~祝你們學習愉快~謝謝~git
CALayer Geometry講得是圖層的幾何, 主要內容分別有Layout, anchorPoint, Coordinate Systems, Hit Testing, Automatic Layout, 待咱們一一去了解和學習~github
Layout顧名思義就是佈局的意思, 這一段講的是視圖和圖層簡單的佈局, 好比UIView裏面有frame, bounds, center, CALayer裏面有frame, bounds, position. 這裏對佈局屬性進行一下解釋: 微信
- frame: 表明了的是圖層外部的一個座標軸, 也是相對於父視圖.
- bounds: 表明了的是內部座標軸, 它的原點永遠都是**{0, 0}**, 是相對於本視圖的位置和大小.
- center以及position: 表明的是現對於父視圖所在的位置, 也就是咱們常常所說的中心點, 可是呢, 其實這個說法不太對, 實際上是應該相對於父視圖anchorPoint屬性所在的位置纔對,anchorPoint屬性咱們後面再解釋, 如今讓它打一下醬油先.
咱們來看看Demo中的實例:框架
- (void)viewDidLoad {
[super viewDidLoad];
[self logViewAndLayer];
}
- (void)logViewAndLayer {
NSLog(@"View X: %f", self.view.center.x);
NSLog(@"View Y: %f", self.view.center.y);
NSLog(@"View Frame Width: %f", self.view.frame.size.width);
NSLog(@"View Frame Height: %f", self.view.frame.size.height);
NSLog(@"View Bounds Width: %f", self.view.bounds.size.width);
NSLog(@"View Bounds Height: %f", self.view.bounds.size.height);
NSLog(@"\n");
NSLog(@"Layer X: %f", self.view.layer.position.x);
NSLog(@"Layer Y: %f", self.view.layer.position.y);
NSLog(@"Layer Frame Width: %f", self.view.layer.frame.size.width);
NSLog(@"Layer Frame Height: %f", self.view.layer.frame.size.height);
NSLog(@"Layer Bounds Width: %f", self.view.layer.bounds.size.width);
NSLog(@"Layer Bounds Height: %f", self.view.layer.bounds.size.height);
}
複製代碼
在這裏, 咱們看到layer的frame, bounds, position和UIView的frame, bounds, center都是一致的. 可是在UIView裏, frame, bounds, center這三個屬性只是存取方法罷了, 當UIView對frame屬性進行操做時, 實際上是對CALayer的frame進行操做, 是不可以脫離了CALayer的去改變視圖的frame. frame這個屬性對於UIView和CALayer來講, 它是一個比較玄的屬性, 它的值是根據UIView或者是CALayer的bounds, center/position, transform計算所獲得的一個值, 一旦你去改變了bounds, center/position, transform其中一個值, frame也會隨之而改變, 固然啦, 你去改變frame, bounds, center/position, transform這三個值也會跟着改變的. PS: 這裏還須要提一點, 若是你對CALayer進行了transform的旋轉或者是縮放時, frame所顯示的寬高與bounds的寬高就不會再同樣了.佈局
直接來看Demo吧:學習
- (void)layerTransform {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 100, 100)];
view.backgroundColor = [UIColor redColor];
[self.view addSubview:view];
view.layer.transform = CATransform3DMakeRotation(M_PI_4, 1, 1, 0.5);
NSLog(@"View X: %f", view.center.x);
NSLog(@"View Y: %f", view.center.y);
NSLog(@"View Frame Width: %f", view.frame.size.width);
NSLog(@"View Frame Height: %f", view.frame.size.height);
NSLog(@"View Bounds Width: %f", view.bounds.size.width);
NSLog(@"View Bounds Height: %f", view.bounds.size.height);
NSLog(@"\n");
NSLog(@"Layer X: %f", view.layer.position.x);
NSLog(@"Layer Y: %f", view.layer.position.y);
NSLog(@"Layer Frame Width: %f", view.layer.frame.size.width);
NSLog(@"Layer Frame Height: %f", view.layer.frame.size.height);
NSLog(@"Layer Bounds Width: %f", view.layer.bounds.size.width);
NSLog(@"Layer Bounds Height: %f", view.layer.bounds.size.height);
}
複製代碼
anchorPoint這個屬性名, 也叫做錨點, 指的一個UIView和CALayer相對於父視圖的位置參考點, 左上角爲**{0, 0}, 居中爲{0.5, 0.5}, 右下角爲{1, 1}, 默認都是位於父視圖層的中點, 也就是{0.5, 0.5}. 在UIView當中, anchorPoint屬性並無公開出來, 你只能操做center這個值, 間接性的給anchorPoint賦值, CALayer也能夠經過改變position來給anchorPoint進行賦值, 從而改變CALayer的frame**. 這裏還須要說起一點, 若是你改變了CALayer的anchorPoint, 那麼CALayer的frame也會被改變, 那麼anchorPoint屬性所顯示的值不再是該CALayer的中心點, 但bounds,position這些值並不會被改變.ui
咱們直接來看看Demo吧~spa
- (void)viewAnchorPoint {
UIView *viewOne = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
viewOne.backgroundColor = [UIColor grayColor];
NSLog(@"before X: %f", viewOne.layer.frame.origin.x);
NSLog(@"before Y: %f", viewOne.layer.frame.origin.y);
// change anchorPoint
viewOne.layer.anchorPoint = CGPointMake(0.1f, 0.1f);
NSLog(@"After X: %f", viewOne.layer.frame.origin.x);
NSLog(@"After Y:%f", viewOne.layer.frame.origin.y);
UIView *viewTwo = [[UIView alloc] init];
viewTwo.backgroundColor = [UIColor redColor];
viewTwo.bounds = CGRectMake(0, 0, 100, 100);
viewTwo.center = viewOne.center;
[self.view addSubview:viewOne];
[self.view addSubview:viewTwo];
}
複製代碼
通常來說, 可以用到anchorPoint屬性的場景, 大多數都是在與圖層疊加方面, 否則基本佈局就已經夠用了, 固然, 這只是在簡單頁面的基礎上來說而已, 仍是看我的需求吧.3d
Coordinate Systems也叫做座標系, 和UIView同樣,CALayer也是相對於父圖層而且按照層級關係來擺放, 若是父圖層的位置發生了變化, 那麼子圖層也會隨之而改變. 但某些狀況下, 你想要知道這個圖層的絕對位置, 或者是相對於另外一個圖層的位置時, 你就可使用CALayer所提供的幾個API進行處理了:
- (CGPoint)convertPoint:(CGPoint)point toView:(nullable UIView *)view;
- (CGPoint)convertPoint:(CGPoint)point fromView:(nullable UIView *)view;
- (CGRect)convertRect:(CGRect)rect toView:(nullable UIView *)view;
- (CGRect)convertRect:(CGRect)rect fromView:(nullable UIView *)view;
複製代碼
這些方法,能夠採起任何一層的座標系中所定義的點或矩形,並將其轉換到另外一個座標系。
這裏再普及一個概念, 咱們都知道在iOS當中CALayer的position是在父圖層的左上角, 但在Mac OS當中, 通常來講是在左下角, 而Core Animation爲了解決這個問題, 提供了一個BOOL屬性geometryFlipped來適配這兩種狀況, 它決定了一個圖層的座標是否相對於父圖層且垂直翻轉, 若是在iOS上把geometryFlipped設置爲YES的話, 那麼圖層的排版就會沿着底部來排版, 而不是咱們一般所看到的那樣, 包括它的子圖層也是如此, 除非你再把子圖層裏的geometryFlipped屬性設置爲YES.
直接看Demo吧:
- (void)layerGeometryFlipped {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
view.backgroundColor = [UIColor blueColor];
view.layer.geometryFlipped = YES;
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(0, 0, 25, 25);
layer.backgroundColor = [UIColor redColor].CGColor;
[view.layer addSublayer:layer];
[self.view addSubview:view];
}
複製代碼
其實CALayer除了有UIView常規的二維座標軸以外, 還有一個Z座標軸, 也就是說CALayer存在於三維空間當中. 除了咱們以前接觸過的position和anchorPoint以外, 還有兩個Z座標軸的屬性, 叫作zPosition和anchorPointZ, 且都是CGFloat類型. zPosition這個屬性在一般狀況下並不經常使用, 在後面咱們涉及到一些3D轉換的內容時纔會去使用這個zPosition屬性, zPosition屬性除了用來作3D轉換以外, 更可能是用來改變CALayer的顯示順序了.
來看看Demo吧
- (void)viewsZPosition {
UIView *greenView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
greenView.backgroundColor = [UIColor greenColor];
greenView.layer.zPosition = 1.0f;
UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(150, 150, 100, 100)];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:greenView];
[self.view addSubview:redView];
}
複製代碼
在以前的文章裏, 咱們知道CALayer並不能處理任何響應鏈的事件, 因此不能直接去處理觸摸事件或者手勢, 可是呢, CALayer提供了一些方法可讓你知道你知道你點擊了這個CALayer.
- (void)containsPoint;
- (void)hitTest;
複製代碼
咱們直接來看看代碼吧:
- (void)hitTestingLayer {
_backgroundView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
_backgroundView.backgroundColor = [UIColor grayColor];
_blueLayer = [CALayer layer];
_blueLayer.backgroundColor = [UIColor blueColor].CGColor;
_blueLayer.frame = CGRectMake(25, 25, 50, 50);
[_backgroundView.layer addSublayer:_blueLayer];
[self.view addSubview:_backgroundView];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
CGPoint point = [[touches anyObject] locationInView:self.view];
CALayer *layer = [self.backgroundView.layer hitTest:point];
UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:@"你點擊了屏幕"
message:@""
delegate:nil
cancelButtonTitle:@"肯定"
otherButtonTitles:nil, nil];
if (layer == self.blueLayer) {
alerView.message = @"你點中了藍色的Layer";
} else if (layer == self.backgroundView.layer){
alerView.message = @"你點中了灰色的Layer";
} else {
alerView.message = @"你點中了其餘的Layer";
}
[alerView show];
}
複製代碼
所謂的Automatic Layout就是咱們常常所說的自動佈局, 自動佈局是蘋果在iOS 6的時候引入進來的一套佈局機制, 但當時好像Xcode不夠給力, 最終並無獲得很好的運用. 在Mac OS當中CALayer有一個叫作layoutManager的屬性, 它能夠經過CALayoutManager協議和CAConstraintLayoutManger類來實現自動佈局, 但這個東東在iOS上並不能使用, 詳細我也不太瞭解. 再補充多一點, 在咱們對UIView使用自動佈局的時候, 你能夠直接使用UIView提供的UIViewAutoresizingMask和NSLayoutConstraint兩個API進行佈局, 但若是你要對一個CALayer進行佈局的話, 那麼最簡單的方式就是使用CALayerDelegate提供的API:
- (void)layoutSublayersOfLayer:(CALayer *)layer;
複製代碼
當CALayer的bounds發生了改變或者**- (void)setNeedsLayout;方法被調用的時候, 上面所說起的API就會被調用, 這樣子你就能夠在上面所說起的API裏手動去調整CALayer的佈局. 但這樣子會有一個致命的問題, 因爲CALayer並無autoresizingMask以及constraints屬性, 因此不能像UIView那樣自適應屏幕旋轉, 這也是爲何絕大多數人選擇使用UIView進行佈局的緣由之一. 這裏我就很少說了, 自動佈局的知識點在谷歌搜一大堆, 有神馬xib的, storyboard的, 還有純代碼的, 可是呢, 純代碼的自動佈局我只服Masonry, 也有Swift**版本的, 你們能夠自行去了解一下, 使用教程神馬的, 在谷歌搜搜也是有一堆的, 這裏就不作介紹了~
說了那麼多, 常規的總結一下, 此次咱們更深刻的瞭解了CALayer幾何圖層, 其中包括:
- Layout:CALayer的常規佈局
- AnchorPoint: CALayer的錨點
- Coordinate Systems: CALayer的座標軸, 其中包括如何翻轉CALayer的幾何圖層, 還有就是Z座標軸.
- Hit Testing: CALayer如何知道用戶是否點擊該圖層
- Automatic Layout: 有關於CALayer以及UIView的自動佈局, 以及一個第三方的佈局框架
項目地址: https://github.com/CainRun/CoreAnimation