图层几何学 -- iOS Core Animation 系列二
《圖層樹和寄宿圖 -- iOS Core Animation 系列一》介紹了圖層的基礎知識和一些屬性方法。這篇主要內容是學習下圖層在父圖層上怎么控制位置和尺寸的。
1.布局
首先看一張例圖:
對于圖上的frame、bounds、center、postion的概念我就不贅述了。如果有不明白的自行搜索下了解一下。
frame代表了圖層的外部坐標(也就是在父圖層上占據的空間),bounds是內部坐標({0, 0}通常是圖層的左上角),center和position都代表了相對于父圖層anchorPoint所在的位置視圖的frame、bounds、center屬性僅僅是存取方法,當操縱視圖的frame時,實際上是在改變視圖對應的CALayer的frame, 不能獨立于圖層之外改變視圖的frame.
如果對圖層做了變換,比如旋轉縮放等。frame的值實際指的是圖層旋轉之后整個軸對齊的矩形區域。此時frame的寬高可能和bounds的寬高不一致:
2.錨點
默認來說,anchorPoint位于圖層的中點。這個屬性沒有被UIView直接暴露出來。但是圖層的anchorPoint可以被移動。我們可以把anchorPoint置于圖層frame的左上角。將會出現下圖右側的情況:
注意上圖,改變anchorPoint后position的值并沒變。
和系列一中提到的contentsRect類似,anchorPoint用單位坐標來表示(默認情況是{0.5, 0.5})。可以通過指定x和y值小于0或者大于1,使它放置在圖層范圍之外。
2.1 示例
為了學習這個anchorPoint屬性,下面創建一個鬧鐘的示例demo。
資源文件我是從原文上截圖下來的
創建4個UIImageView并設置好約束(都是居中顯示)。
我們用NSTimer來更新鬧鐘,使用視圖的transform屬性來旋轉鐘表。
代碼如下:
運行項目如下圖:
除了指針圖片的位置,其他的都正常。
可能這時候我們最先想到的方法,是調整對應圖片的位置來解決。但是這樣的話,你可以試試,并不能解決問題。不用賣關子了。這時候就是要用到anchorPoint的時候。處理代碼如下:
運行完美。
3. 坐標系
眾所周知,一個圖層的position依賴于父圖層的bounds,如果父圖層移動,所有子圖層也會跟著移動。
CALayer也給我們提供了一些獲取一個圖層的絕對位置的方法,或者相對于另一圖層的位置(而不是它當前父圖層的位置):
3.1 z坐標軸
和UIView的二維坐標不同,CALayer存在于一個三維空間中,它還提供了zPostion和anchorPointz屬性。
zPosition屬性大多數不常用,除了三維動畫之外,它最實用的功能是可以改變圖層的顯示順序。
3.2 zPosition演示代碼
我們演示下改變zPosition會怎么改變視圖的顯示順序。
首先我在SB中設置兩個視圖,如下圖:
如果我們不做任何操作,運行后,兩個視圖顯示的順序就是我們現在設置的這樣。但是假如我們對yellowView設置zPosition,哪怕很小的值,都會發現顯示的順序反了。
@interface ViewController () @property (weak, nonatomic) IBOutlet UIView *cyanView; @property (weak, nonatomic) IBOutlet UIView *yellowView;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];self.yellowView.layer.zPosition = 1.f; }現在的顯示效果如下:
雖說圖層基本沒有厚度,但是我們也盡量不要設置zPosition = 0.01f之類的。因為浮點類型的四舍五入可能導致難以察覺的麻煩。
4. Hit Testing
雖說CALayer不關心響應鏈事件,但是它提供了一些方法讓我們處理事件-containsPoint:和-hitTest:。
4.1 -containsPoint:
-containsPoint:接受一個在本圖層坐標系下的CGPoint,如果這個點在圖層frame范圍內就返回YES.我們可以使用這個方法判斷是哪個圖層被觸摸了。
4.1.1 containsPoint 示例
代碼如下:
@interface ViewController () @property (weak, nonatomic) IBOutlet UIView *layerView; @property (nonatomic, strong) CALayer *blueLayer;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];self.blueLayer = [CALayer layer];self.blueLayer.frame = CGRectMake(20.f, 20.f, 100.f, 100.f);self.blueLayer.backgroundColor = [UIColor blueColor].CGColor;[self.layerView.layer addSublayer:self.blueLayer]; }- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {// 獲取觸摸點CGPoint point = [[touches anyObject] locationInView:self.view];// 轉換觸摸點在layerView的圖層的位置point = [self.layerView.layer convertPoint:point fromLayer:self.view.layer];// 判斷是否包含在layerview里面if ([self.layerView.layer containsPoint:point]) {point = [self.blueLayer convertPoint:point fromLayer:self.layerView.layer];if ([self.blueLayer containsPoint:point]) {NSLog(@"點擊藍色圖層");} else {NSLog(@"點擊了白色圖層");}} }運行點擊可以在控制臺看到NSLog的輸出信息。
4.2. -hitTest:
-hitTest:方法同樣接受一個CGPoint參數,但是返回的是圖層本身,而不是BOOL類型。這使我們不用像-containsPoint:一樣每個子圖層去測試點擊的坐標。如果這個點是在最外面的圖層,則返回nil。
4.2.1 hitTest示例
把上面-containsPoint:示例的代碼下面的部分修改一下即可:
- (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(@"點擊藍色圖層");} else if (layer == self.layerView.layer) {NSLog(@"點擊了白色圖層");} } 嘗試修改self.layerView的zPosition,會有不同的結果。有興趣的可以自己測試一下。-- 系列二完 --
總結
以上是生活随笔為你收集整理的图层几何学 -- iOS Core Animation 系列二的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL无法修改字段
- 下一篇: vue使用总结-生命周期篇