iOS CALayer anchorPoint 的應用場景

aron1992發表於2019-04-04

背景

最近在看《ios核心動畫高階技巧》,這本書的中文版本可以在iOS Core Animation: Advanced Techniques中文譯本 這裡找到,看到圖形幾何學這一章,其中講到的 CALayeranchorPoint 屬性,發現不是很瞭解,所以自己發了點時間做了些研究,做了點簡單的總結,主要涉及到以下主題:

  • anchorPoint 是什麼
  • anchorPoint 應用場景

本文的DemoDrawDemo

anchorPoint 是什麼

圖層的anchorPoint通過position來控制它的frame的位置,你可以認為anchorPoint是用來移動圖層的把柄。

上面是《ios核心動畫高階技巧》書中對 anchorPoint 的解釋,只看概念可能會不好理解,下面用兩個例子來解釋anchorPoint的概念。

例一:修改了anchorPoint 對圖層的影響

如圖所示,建立一個Frame為(100, 200, 100, 100)的藍色layer,橙色的layer是修改了anchorPoint為CGPointMake(1, 1)之後的結果

修改anchorPoint對圖層的影響1
修改anchorPoint對圖層的影響1

1、分析藍色layer
預設的anchorPoint是(0.5, 0.5),位於layer的中間,position也是位於layer的中間,layer以中心點為定位點,此時positon的意義是 layer的中心點距離父layer的原點的位置是position.x,position.y ,所以最終的frame位置如圖所示為 {{100, 100}, {100, 100}}

2、分析橙色layer anchorPoint是(1, 1),位於layer的右下角,position也是位於layer的右下角,layer以右下角為定位點,此時positon的意義是 layer的右下角距離父layer的原點的位置是position.x,position.y ,所以最終的frame位置如圖所示為 {{50, 50}, {100, 100}}

3、兩個重要的結論

  1. anchorPoint和position是一一對應的,anchorPoint是相對於layer自身的,是一個單位座標 (0~1, 0~1) ,表示當前layer的定位點是位於layer的什麼位置,定位點決定了layer相對於父layer的關係以及layer的旋轉中心,後面會有旋轉的例子
  2. 只修改了anchorPoint不會使position變化,但是圖層位置會變化,因為定位點的位置改變了,導致最終Layer的Frame改變了,Frame是最終決定Layer位置的。

例子的程式碼如下:

// anchorPoint 對圖層的影響
    layer = [[CALayer alloc] init];
    layer.frame = CGRectMake(100, 200, 100, 100);
    layer.borderColor = [UIColor blueColor].CGColor;
    layer.borderWidth = 1;
    layer.anchorPoint = CGPointMake(0.5, 0.5);
    [self.view.layer addSublayer:layer];
    NSLog(@"layer position = %@, anchorPoint = %@  frame = %@", NSStringFromCGPoint(layer.position), NSStringFromCGPoint(layer.anchorPoint), NSStringFromCGRect(layer.frame));

    {
        CALayer* copyLayer = [self cloneLayer:layer];
        copyLayer.borderColor = [UIColor orangeColor].CGColor;
        [self.view.layer addSublayer:copyLayer];
        // 預設的anchorPoint位置為(0.5, 0.5),和position是對應的
        // 需要移動anchorPoint到layer的右下角,
        // 假設認為anchorPoint的位置是固定的,怎樣才能把anchorPoint定位到位置為(1, 1)
        // 需要把整個的layer往上和往左移動(layer.width * 0.5)個點才能夠做到
        // 這樣會導致frame的origin的變化,也就是layer會往左上角偏移了
        // position的值沒有改變,anchorPoint位置是對應的,下面會進行談論
        copyLayer.anchorPoint = CGPointMake(1, 1);
        NSLog(@"layer position = %@, anchorPoint = %@  frame = %@", NSStringFromCGPoint(copyLayer.position), NSStringFromCGPoint(copyLayer.anchorPoint), NSStringFromCGRect(copyLayer.frame));
        
        // 回到這裡,繼續討論anchorPoint引起的frame的變化而position不變
        // anchorPoint是layer定位點(包括旋轉和縮放,是相對於layer自身的,是一個單位座標的點)
        // position是和anchorPoint對應的點,是layer相對於父layer的定位點,
        // anchorPoint=(0.5, 0.5),layer的中心位置相對於父layer原點的位置為position
        // 因為在只修改anchorPoint的情況下不改變position的值,所以:
        // anchorPoint=(1, 1),layer的右下角位置相對於父layer原點的位置為position
    }
複製程式碼

例二:Frame不變,修改了anchorPoint 對圖層的影響

如圖所示,建立一個Frame為(100, 300, 100, 100)的藍色layer,橙色的layer是修改了anchorPoint為CGPointMake(0, 0)之後但是Frame不變的結果,紅色的layer是修改了anchorPoint為CGPointMake(1, 1)之後但是Frame不變的結果

Frame不變,修改了anchorPoint 對圖層的影響
Frame不變,修改了anchorPoint 對圖層的影響

1、分析橙色Layer
在保持Frame不變的情況下,anchorPoint是(0, 0),位於layer的左上角,position也是位於layer的左上角,layer以左上角為定位點,此時positon的意義是 layer的左上角距離父layer的原點的位置是position.x,position.y ,對layer進行45°的旋轉之後,如圖Layer以左上角做了順時針的45°旋轉。

2、分析紅色Layer
在保持Frame不變的情況下,anchorPoint是(1, 01),位於layer的右下角,position也是位於layer的右下角,layer以右下角為定位點,此時positon的意義是 layer的右下距離父layer的原點的位置是position.x,position.y ,對layer進行45°的旋轉之後,如圖Layer以右下角做了順時針的45°旋轉。

例子的程式碼如下:

    // 在Frame不變的情況下討論anchorPoint對position的影響
    // 可以這麼認為,position是因變數,anchorPoint是自變數
    // anchorPoint和position是一一對應的,是同一個點
    layer = [[CALayer alloc] init];
    layer.borderColor = [UIColor blueColor].CGColor;
    layer.borderWidth = 1;
    layer.anchorPoint = CGPointMake(1, 1);
    layer.frame = CGRectMake(100, 300, 100, 100);
    [self.view.layer addSublayer:layer];
    
    {
        CALayer* copylayer = [self cloneLayer:layer];
        copylayer.borderColor = [UIColor orangeColor].CGColor;
        [self.view.layer addSublayer:copylayer];
        
        // Frame不變,anchorPoint設定到右下角,positon位置也會變到右下角{150, 500}
        copylayer.anchorPoint = CGPointMake(1, 1);
        copylayer.frame = CGRectMake(100, 300, 100, 100);
        NSLog(@"layer.position = %@", NSStringFromCGPoint(copylayer.position));
        
        // 旋轉
        copylayer.affineTransform = CGAffineTransformRotate(copylayer.affineTransform, M_PI_4);
    }
    
    {
        CALayer* copylayer = [self cloneLayer:layer];
        copylayer.borderColor = [UIColor orangeColor].CGColor;
        [self.view.layer addSublayer:copylayer];
        
        // Frame不變,anchorPoint設定到坐上角,positon位置也會變到坐上角{50, 400}
        copylayer.anchorPoint = CGPointMake(0, 0);
        copylayer.frame = CGRectMake(100, 300, 100, 100);
        NSLog(@"layer.position = %@", NSStringFromCGPoint(copylayer.position));
        
        // 旋轉
        copylayer.affineTransform = CGAffineTransformRotate(copylayer.affineTransform, M_PI_4);
    }
複製程式碼

anchorPoint 應用場景

例子來自於《ios核心動畫高階技巧》書中國的 時鐘錶盤例子

例子可以在我的git上找到ClockFace

初始的時鐘指標在錶盤中的位置如下:

初始的時鐘指標在錶盤中的位置
初始的時鐘指標在錶盤中的位置

// 秒針
layer frame:{{124, 77}, {8, 102}} anchorPoint:{0.5, 0.5} position:{128, 128}
// 分針
layer frame:{{118, 75}, {20, 106}} anchorPoint:{0.5, 0.5} position:{128, 128}
// 時針
 layer frame:{{113, 81}, {30, 94}} anchorPoint:{0.5, 0.5} position:{128, 128}
複製程式碼

沒有設定anchorPoint的指標走動效果:

沒有設定anchorPoint的指標走動效果
沒有設定anchorPoint的指標走動效果

根據以上的分析我們知道原因是預設的anchorPoint為{0.5, 0.5},也就是旋轉中心是指標的中心點導致的效果如此,知道原因了修改也不難了,最簡單的做法就是把anchorPoint設定為{0.5, 0.9}即可。

    //adjust anchor points
    self.secondHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
    self.minuteHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
    self.hourHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
複製程式碼

對應的指標的layer變化:

// 秒針
layer frame:{{124, 36.2}, {8, 102}} anchorPoint:{0.5, 0.89} position:{128, 128}
// 分針
layer frame:{{118, 32.6}, {20, 106}} anchorPoint:{0.5, 0.8} position:{128, 128}
// 時針
layer frame:{{113, 43.4}, {30, 94}} anchorPoint:{0.5, 0.89} position:{128, 128}
複製程式碼

效果如下:

設定anchorPoint的指標走動效果
設定anchorPoint的指標走動效果

總結

以上是Layer中anchorPoint屬性的一些總結以及一個應用場景,如有不妥之處敬請指教。

相關文章