使用 CGAffineTransform 旋轉或縮放 layer 時,設定 bounds & position 維持中心點位置

當我們使用 CGAffineTransform 旋轉或縮放 CALayer 時,若想讓 layer 的中心點位置不變,記得要設定 layer 的 bounds & position。

以下我們舉一個例子說明,首先我們在黃色 view 上加入紅色正方形的 layer。

let view = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
view.backgroundColor = .yellow
let path = UIBezierPath(rect: CGRect(x: 100, y: 100, width: 100, height: 100))
let rectangleLayer = CAShapeLayer()
rectangleLayer.fillColor = CGColor(red: 1, green: 0, blue: 0, alpha: 1)
rectangleLayer.path = path.cgPath
view.layer.addSublayer(rectangleLayer)
view

旋轉

對 rectangleLayer 呼叫 setAffineTransform 旋轉 10 度。

let view = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
view.backgroundColor = .yellow
let path = UIBezierPath(rect: CGRect(x: 100, y: 100, width: 100, height: 100))
let rectangleLayer = CAShapeLayer()
rectangleLayer.fillColor = CGColor(red: 1, green: 0, blue: 0, alpha: 1)
rectangleLayer.path = path.cgPath
rectangleLayer.setAffineTransform(CGAffineTransform(rotationAngle: .pi / 180 * 10))
view.layer.addSublayer(rectangleLayer)
view

令人失望的,紅色正方形旋轉了,但它的位置有點偏移,它並不是繞著自己的中心點旋轉。

let view = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
view.backgroundColor = .yellow
let path = UIBezierPath(rect: CGRect(x: 100, y: 100, width: 100, height: 100))
let rectangleLayer = CAShapeLayer()
rectangleLayer.fillColor = CGColor(red: 1, green: 0, blue: 0, alpha: 1)
rectangleLayer.path = path.cgPath

let boundingBox = path.cgPath.boundingBox
rectangleLayer.bounds = boundingBox
rectangleLayer.position = CGPoint(x: boundingBox.midX, y: boundingBox.midY)
rectangleLayer.setAffineTransform(CGAffineTransform(rotationAngle: .pi / 180 * 10))
view.layer.addSublayer(rectangleLayer)
view

問題出在 rectangleLayer 的 bounds 和 position 沒有設定。當我們加入以下程式設定後,rectangleLayer 即可正常地繞著自己的中心點旋轉。

let boundingBox = path.cgPath.boundingBox
rectangleLayer.bounds = boundingBox
rectangleLayer.position = CGPoint(x: boundingBox.midX, y: boundingBox.midY

縮放

將紅色正方形變成一半的大小,結果中心點位置跑掉了。

let view = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
view.backgroundColor = .yellow
let path = UIBezierPath(rect: CGRect(x: 100, y: 100, width: 100, height: 100))

let rectangleLayer = CAShapeLayer()
rectangleLayer.fillColor = CGColor(red: 1, green: 0, blue: 0, alpha: 1)
rectangleLayer.path = path.cgPath
rectangleLayer.setAffineTransform(CGAffineTransform(scaleX: 0.5, y: 0.5))
view.layer.addSublayer(rectangleLayer)
view

設定 layer 的 bounds & position 後,layer 順利維持原本的中心點位置。

let view = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
view.backgroundColor = .yellow
let path = UIBezierPath(rect: CGRect(x: 100, y: 100, width: 100, height: 100))

let rectangleLayer = CAShapeLayer()
rectangleLayer.fillColor = CGColor(red: 1, green: 0, blue: 0, alpha: 1)
rectangleLayer.path = path.cgPath
let boundingBox = path.cgPath.boundingBox
rectangleLayer.bounds = boundingBox
rectangleLayer.position = CGPoint(x: boundingBox.midX, y: boundingBox.midY)
rectangleLayer.setAffineTransform(CGAffineTransform(scaleX: 0.5, y: 0.5))
view.layer.addSublayer(rectangleLayer)
view

--

--

彼得潘的 iOS App Neverland
彼得潘的 Swift iOS App 開發問題解答集

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com