CALayer常用屬性整理(二)

接著捋~

錨點(anchorPoint)

錨點也需要使用單位坐標(biāo),默認是(0.5,0.5),也就是中心點。當(dāng)把錨點值設(shè)置為(0,0)時錨點將位于圖層左上角,(1,1)時為右下角,以此類推。

關(guān)于錨點的使用書中給了一個時鐘的例子,而在開發(fā)中我唯一使用到錨點的場景是在制作抽獎的轉(zhuǎn)盤時,為了給獎品的圖片做旋轉(zhuǎn),需要更改圖片的錨點來處理(其實和時鐘例子的用法幾乎一樣)。大概就是下面圖片中的效果。

anchorPoint.png

至于轉(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)該是這樣的。

期望的效果圖.png

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

實際的效果圖.png

看到這樣的效果,老司機肯定會暗罵一聲“湊,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就不貼代碼了

containsPoint的例子.png

然后在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");
  }
}

代碼看起來比之前的好理解多了,而且也變得更簡潔。這次就寫這么多吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容