接著捋~
錨點(anchorPoint)
錨點也需要使用單位坐標(biāo),默認是(0.5,0.5),也就是中心點。當(dāng)把錨點值設(shè)置為(0,0)時錨點將位于圖層左上角,(1,1)時為右下角,以此類推。
關(guān)于錨點的使用書中給了一個時鐘的例子,而在開發(fā)中我唯一使用到錨點的場景是在制作抽獎的轉(zhuǎn)盤時,為了給獎品的圖片做旋轉(zhuǎn),需要更改圖片的錨點來處理(其實和時鐘例子的用法幾乎一樣)。大概就是下面圖片中的效果。

至于轉(zhuǎn)盤的實現(xiàn)方式和代碼,等把該整理的都整理完,如果還想再寫點東西的話再附上一篇抽獎轉(zhuǎn)盤的實現(xiàn)吧= =。
不同坐標(biāo)系之間的轉(zhuǎn)換方法
就是這幾個方法:
- (CGPoint)convertPoint:(CGPoint)point fromLayer:(CALayer *)layer;
- (CGPoint)convertPoint:(CGPoint)point toLayer:(CALayer *)layer;
- (CGRect)convertRect:(CGRect)rect fromLayer:(CALayer *)layer;
- (CGRect)convertRect:(CGRect)rect toLayer:(CALayer *)layer;
拿第一個方法為例,意思按我自己的理解大概就是將點(point)從圖層(layer)中的值轉(zhuǎn)換到當(dāng)前圖層,并返回轉(zhuǎn)換后的值。第二個方法:將點(point)從他所在的圖層轉(zhuǎn)移到指定圖層(layer),并返回轉(zhuǎn)以后的值。
或者可以看下面的一個例子。
UIView *outView = [[UIView alloc] initWithFrame:CGRectMake(30, 30, 300, 300)];
outView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:outView];
CALayer *innerLayer = [CALayer layer];
innerLayer.backgroundColor = [UIColor redColor].CGColor;
innerLayer.frame = CGRectMake(0, 0, 50, 50);
innerLayer.position = outView.layer.position;
[outView.layer addSublayer:innerLayer];
首先定義了一個外層的outView,然后在outView.layer上添加了一個新的圖層叫innerLayer,并且讓innerLayer在outView.layer上居中顯示。我們期望這段代碼執(zhí)行后看到的效果應(yīng)該是這樣的。

但是實際上運行起來會是這樣的。

看到這樣的效果,老司機肯定會暗罵一聲“湊,xxx忘寫了”。然后萌新就會有點懵“麻辣雞,這是什么鬼,說好的居中呢”
至于為什么會出現(xiàn)上圖中的效果,是因為白色View中position的值是以它的父視圖(也就是灰色的view)來計算的,在本例中這個值是(180,210)。也就是說白色view的position的值是在父視圖坐標(biāo)系中計算出來的。那么這個時候直接把這個值賦給紅色layer(innerLayer)的position來達到居中的目的肯定是做不到的。如果我沒解釋清楚的話直接看position屬性(或者UIView的center屬性,效果是一樣的)的官方解釋可能會更清晰。
執(zhí)行“innerLayer.position = outView.layer.position;”這句代碼在本例中等同于“innerLayer.position = CGPointMake(180, 210);”。因為innerLayer的布局是依賴它的父圖層(也就是outView.layer)坐標(biāo)系,而在outView的坐標(biāo)系中,中心點顯然不是(180,210)這個點(明顯是(150,150)的?。?,所以,導(dǎo)致innerLayer跑偏了。
這個問題就是因為坐標(biāo)系使用混亂導(dǎo)致的。下面我們將innerLayer的代碼這么寫:
CALayer *innerLayer = [CALayer layer];
innerLayer.backgroundColor = [UIColor redColor].CGColor;
innerLayer.frame = CGRectMake(0, 0, 50, 50);
CGPoint innerCenter = [self.view.layer convertPoint:outView.layer.position toLayer:outView.layer];
innerLayer.position = innerCenter;
[outView.layer addSublayer:innerLayer];
與之前的代碼相比,新加入了一句代碼“CGPoint innerCenter = [self.view.layer convertPoint:outView.layer.position toLayer:outView.layer];”。這句代碼用來把outView.layer.position的值從他的父視圖坐標(biāo)系轉(zhuǎn)換到outView.layer的坐標(biāo)系中。innerLayer就可以正常顯示了。
zPosition
描述了圖層在z軸上的位置,可以用來改變圖層顯示順序,注意僅僅是顯示順序。
zPosition默認是0,數(shù)值越高它的顯示優(yōu)先級也越高,但是不能改變響應(yīng)優(yōu)先級
geometryFlipped
geometryFlipped決定了一個圖層的坐標(biāo)是否相對于父圖層垂直翻轉(zhuǎn),默認情況下是NO,也就是從左上角開始繪制,當(dāng)把值改為YES的時候這個圖層和他的子圖層將會被垂直翻轉(zhuǎn),也就是從左下角開始繪制。
hitTest方法
在說hitTest方法之前要先說CALayer的另一個方法:
- (BOOL)containsPoint:(CGPoint)p;
在CALayer屬性整理(一)里面開頭就提到過CALayer不處理交互,但是它仍提供了一些方法來幫助我們處理交互。上面的方法接受一個在本圖層坐標(biāo)系下的CGPoint,注意是本圖層坐標(biāo)系!如果這個點在該圖層的frame范圍內(nèi)返回YES,反之返回NO。
書上有一個這樣的例子:首先界面是這樣的,一個view上加了個layer就不貼代碼了

然后在vc里面重寫一下touchesBegan方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//獲取self.view坐標(biāo)系中的觸摸點
CGPoint point = [[touches anyObject] locationInView:self.view];
//把獲取到的點從self.view.layer中轉(zhuǎn)換到self.aView.layer的坐標(biāo)系中。
point = [self.aView.layer convertPoint:point fromLayer:self.view.layer];
//判斷self.aView.layer是否包含這個點
if ([self.aView.layer containsPoint:point]) {
//繼續(xù)把點從self.aView.layer中轉(zhuǎn)移到self.blueLayer的坐標(biāo)系
point = [self.blueLayer convertPoint:point fromLayer:self.aView.layer];
if ([self.blueLayer containsPoint:point]) {
NSLog(@"Inside Blue Layer");
} else {
NSLog(@"Inside White Layer");
}
}
}
可以看到要想使用containsPoint方法必須要先轉(zhuǎn)換坐標(biāo)系,這樣做還是有點麻煩的。好在我們還有hitTest方法可以用。hitTest方法同樣需要傳入一個點,但是他的返回值不再是布爾值。而是一個可以為空的CALayer。
- (nullable CALayer *)hitTest:(CGPoint)p;
當(dāng)我們傳入一個點,這個點在哪一個layer上就會返回這個layer,如果在這個layer的子圖層上則會返回子圖層,如果這個點在最外面圖層的范圍之外,則返回nil。使用hitTest方法之后上面的代碼可以改成這樣:
- (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) {
NSLog(@"Inside Blue Layer");
} else if (layer == self.layerView.layer) {
NSLog(@"Inside White Layer");
}
}
代碼看起來比之前的好理解多了,而且也變得更簡潔。這次就寫這么多吧。