布局
UIView有三個比較重要的布局屬性:frame娘侍、bounds技矮、center。CALayer對應(yīng)地叫做:frame估脆、bounds钦奋、position。雖然UIView的center和CALayer的position屬性代表同樣的值疙赠。
- frame代表了圖層的外部坐標付材,也就是在父圖層上占據(jù)的空間;
- bounds代表了圖層的內(nèi)部坐標圃阳,{0,0通常是圖層的左上角}厌衔;
- center和position代表了相對于父圖層anchorPoint所在的位置;
視圖的frame和圖層的frame是相關(guān)聯(lián)的捍岳,視圖的frame發(fā)生改變富寿,該視圖對應(yīng)的CALayer的frame也會發(fā)生變化。
frame屬性值是根據(jù)bounds锣夹、position和transform計算出來的页徐,當其中任意一個值發(fā)生變化,frame就會發(fā)生變化银萍,當然改變frame值变勇,也會影響到他們當中的值。
當對圖層做變換的時候贴唇,例如旋轉(zhuǎn)或縮放搀绣,frame實際上代表了覆蓋在圖層旋轉(zhuǎn)之后的整個軸對齊的矩形區(qū)域,因此frame的寬高可能和bounds的寬高不再一致了戳气。
錨點
視圖的center屬性和圖層的position屬性都指定anchorPoint相對于父圖層的位置链患。
默認情況下,anchorPoint位于圖層的中點物咳,因此圖層將會以這個點為中心放置锣险。
anchorPoint用單位坐標來描述蹄皱,也就是圖層的相對坐標览闰。因為圖層的左上角是{0,0}芯肤,右下角是{1,1},所以anchorPoint的默認坐標是{0.5,0.5}压鉴。
坐標系
和視圖一樣崖咨,圖層在圖層樹當中也是相對于父圖層按層級關(guān)系放置,一個圖層的position依賴于它父圖層的bounds油吭,如果父圖層發(fā)生了移動击蹲,它的所有子圖層也會跟著移動。
Z坐標軸
和UIView嚴格的二維坐標系不同婉宰,CALayer存在于一個三維空間中歌豺。CALayer還有另外兩個屬性:zPosition和anchorPointZ,二者都是在Z軸上描述圖層位置的浮點類型心包。
通常圖層是根據(jù)它們子圖層的sublayers出現(xiàn)的順序來繪制的类咧。
下面這個示例,是在Interface Builder中放置了一對視圖蟹腾,其中綠色視圖會被繪制在紅色視圖的后面痕惋。
當然也可以提高綠色視圖的zPosition來改變繪圖的順序。
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var greenView: UIView!
@IBOutlet weak var redView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.greenView.layer.zPosition = 1.0
}
}
Hit Testing
CALayer并不關(guān)心任何響應(yīng)鏈事件娃殖,所以不能直接處理觸摸事件或手勢值戳。但是CALayer包含了兩個方法幫助我們處理事件:containsPoint()和hitTest()。
containsPoint(_ p: CGPoint)方法接收一個在本圖層坐標系下的CGPoint炉爆,如果這個點在圖層的frame范圍內(nèi)就返回true堕虹。
import UIKit
class ViewController: UIViewController {
@IBOutlet var layerView: UIView!
private var blueLayer = CALayer()
override func viewDidLoad() {
super.viewDidLoad()
self.blueLayer.frame = CGRectMake(50.0, 50.0, 100.0, 100.0)
self.blueLayer.backgroundColor = UIColor.blueColor().CGColor
self.layerView.layer.addSublayer(self.blueLayer)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
//get touch position relation to main view
var point = touches.first?.locationInView(self.view)
//convert point to the white layer's coordinates
point = self.layerView.layer.convertPoint(point!, fromLayer: self.view.layer)
//get layer using containsPoint method
if self.layerView.layer.containsPoint(point!) {
//convert point to blueLayer's coordinates
point = self.blueLayer.convertPoint(point!, fromLayer: self.layerView.layer)
if self.blueLayer.containsPoint(point!) {
UIAlertView(title: "Inside Blue Layer", message: nil, delegate: nil, cancelButtonTitle: "OK").show()
} else {
UIAlertView(title: "Inside White Layer", message: nil, delegate: nil, cancelButtonTitle: "OK").show()
}
}
}
}
hitTest方法同樣接受一個CGPoint類型參數(shù),但是返回值不是BOOL類型芬首,而是圖層本身鲫凶,或者包含這個坐標點的葉子節(jié)點圖層。
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var layerView: UIView!
private var blueLayer = CALayer()
override func viewDidLoad() {
super.viewDidLoad()
self.blueLayer.frame = CGRectMake(50, 50, 100, 100)
self.blueLayer.backgroundColor = UIColor.blueColor().CGColor
self.layerView.layer.addSublayer(self.blueLayer)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
//get touch position
let point = touches.first?.locationInView(self.view)
//get touch layer
let layer = self.layerView.layer.hitTest(point!)
//get layer using hitTest
if layer == self.blueLayer {
UIAlertView(title: "Inside Blue Layer", message: nil, delegate: nil, cancelButtonTitle: "OK").show()
} else if layer == self.layerView.layer {
UIAlertView(title: "Inside White Layer", message: nil, delegate: nil, cancelButtonTitle: "OK").show()
}
}
}
注意:當調(diào)用圖層的hitTest方法時衩辟,測試的順序嚴格依賴圖層樹當中的圖層順序螟炫。