如何在 CALayer 上进行转换?

How to do transforms on a CALayer?

在写这个问题之前,我

但是,我仍然无法理解如何在 上进行基本变换。很难找到平移、旋转和缩放的解释和简单示例。

今天我终于决定坐下来,做一个测试项目,把它们搞清楚。我的回答如下。

备注:

基础知识

您可以在图层上执行多种不同的变换,但基本的是

  • 翻译(移动)
  • 规模
  • 旋转

要在 CALayer 上进行变换,请将图层的 transform 属性 设置为 CATransform3D 类型。例如,要翻译一个图层,您可以这样做:

myLayer.transform = CATransform3DMakeTranslation(20, 30, 0)

单词Make用于创建初始转换的名称:CATransform3DMakeTranslation。应用的后续转换省略了 Make。例如,请参阅此旋转后跟翻译:

let rotation = CATransform3DMakeRotation(CGFloat.pi * 30.0 / 180.0, 20, 20, 0)
myLayer.transform = CATransform3DTranslate(rotation, 20, 30, 0)

现在我们已经了解了如何进行转换,让我们看一些示例来了解如何进行每个转换。不过,首先,我将展示我是如何设置项目的,以防你也想尝试一下。

设置

对于以下示例,我设置了一个单视图应用程序,并向故事板添加了一个 UIView 和浅蓝色背景。我使用以下代码将视图连接到视图控制器:

import UIKit

class ViewController: UIViewController {
    
    var myLayer = CATextLayer()
    @IBOutlet weak var myView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // setup the sublayer
        addSubLayer()
        
        // do the transform
        transformExample()
    }
    
    func addSubLayer() {
        myLayer.frame = CGRect(x: 0, y: 0, width: 100, height: 40)
        myLayer.backgroundColor = UIColor.blue.cgColor
        myLayer.string = "Hello"
        myView.layer.addSublayer(myLayer)
    }
    
    //******** Replace this function with the examples below ********

    func transformExample() {
        
        // add transform code here ...
        
        
    }

} 

There are many different kinds of CALayer,但我选择使用CATextLayer,这样变换在视觉上会更清晰。

翻译

平移变换移动图层。基本语法是

CATransform3DMakeTranslation(_ tx: CGFloat, _ ty: CGFloat, _ tz: CGFloat)

其中 tx 是 x 坐标的变化,ty 是 y 的变化,tz 是 z 的变化。

例子

在iOS中,坐标系的原点在左上角,所以如果我们想将图层向右移动90点,向下移动50点,我们将执行以下操作:

myLayer.transform = CATransform3DMakeTranslation(90, 50, 0)

注释

  • 请记住,您可以将其粘贴到上面项目代码中的 transformExample() 方法中。
  • 由于我们在这里只处理二维,所以 tz 设置为 0
  • 上图中的红线从原始位置的中心到新位置的中心。这是因为转换是相对于锚点完成的,默认情况下锚点位于图层的中心。

规模

缩放变换拉伸或挤压图层。基本语法是

CATransform3DMakeScale(_ sx: CGFloat, _ sy: CGFloat, _ sz: CGFloat)

其中 sxsysz 分别是缩放(乘以)x、y 和 z 坐标的数字。

例子

如果我们想要一半的宽度和三倍的高度,我们将执行以下操作

myLayer.transform = CATransform3DMakeScale(0.5, 3.0, 1.0)

注释

  • 因为我们只在二维空间工作,所以我们只是将 z 坐标乘以 1.0 以使其不受影响。
  • 上图中的红点代表锚点。注意缩放是如何相对于锚点完成的。也就是说,一切都向着或远离锚点伸展。

旋转

旋转变换围绕锚点(默认为图层中心)旋转图层。基本语法是

CATransform3DMakeRotation(_ angle: CGFloat, _ x: CGFloat, _ y: CGFloat, _ z: CGFloat)

其中 angle 是图层应旋转的弧度角度,xyz 是旋转的轴。将轴设置为 0 会取消围绕该特定轴的旋转。

例子

如果我们想顺时针旋转图层 30 度,我们将执行以下操作:

let degrees = 30.0
let radians = CGFloat(degrees * Double.pi / 180)
myLayer.transform = CATransform3DMakeRotation(radians, 0.0, 0.0, 1.0)

注释

  • 由于我们在二维空间工作,我们只希望 xy 平面绕 z 轴旋转。因此我们将 xy 设置为 0.0 并将 z 设置为 1.0.
  • 这按顺时针方向旋转了图层。我们可以通过将 z 设置为 -1.0.
  • 来逆时针旋转
  • 红点表示锚点所在的位置。旋转是围绕锚点完成的。

多重变换

为了组合多个转换,我们可以像这样使用连接

CATransform3DConcat(_ a: CATransform3D, _ b: CATransform3D)

但是,我们会一个接一个地做。第一个转换将在其名称中使用 Make。以下变换不会使用Make,但它们会将前面的变换作为参数。

例子

这次我们结合了之前的所有三个转换。

let degrees = 30.0
let radians = CGFloat(degrees * Double.pi / 180)

// translate
var transform = CATransform3DMakeTranslation(90, 50, 0)

// rotate
transform = CATransform3DRotate(transform, radians, 0.0, 0.0, 1.0)

// scale
transform = CATransform3DScale(transform, 0.5, 3.0, 1.0)

// apply the transforms
myLayer.transform = transform

注释

  • 转换的顺序很重要。
  • 一切都与锚点(红点)相关。

关于锚点和位置的注意事项

我们在不更改锚点的情况下完成了上述所有转换。但是,有时有必要更改它,例如如果您想围绕中心以外的其他点旋转。但是,这可能有点棘手。

锚点和位置都在同一个地方。锚点以层坐标系的单位表示(默认为0.5, 0.5),位置以superlayer的坐标系表示。它们可以这样设置

myLayer.anchorPoint = CGPoint(x: 0.0, y: 1.0)
myLayer.position = CGPoint(x: 50, y: 50)

如果你只设置锚点而不改变位置,那么框架会改变,这样位置就会在正确的位置。或者更准确地说,框架是根据新的锚点和旧位置重新计算的。这通常会产生意想不到的结果。下面两篇文章对此有很好的讨论。

另见

  • Border, rounded corners, and shadow on a CALayer
  • Using a border with a Bezier path for a layer