如何在CALayer上进行变换?

46

在撰写这个问题之前,我已经:

然而,我仍然不太理解如何在上进行基本的变换。寻找关于平移、旋转和缩放的解释和简单示例一直很困难。

今天,我终于决定坐下来,制作一个测试项目,并弄清楚它们。我的答案如下。

注:

  • 我只会Swift,但如果有其他人想添加Objective-C代码,欢迎。
  • 目前我只关心理解2D变换。
1个回答

139

基础知识

在图层上,你可以进行多种不同的变换,但基本的变换有:

  • 平移(移动)
  • 缩放
  • 旋转

enter image description here

要在 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)

现在我们已经了解了如何进行转换的基础知识,让我们来看一些具体实例。不过,在此之前,我会展示一下如何设置这个项目,以防你也想尝试一下。

设置

对于接下来的例子,我设置了一个 Single View 应用,并在 storyboard 中添加了一个带浅蓝色背景的 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 ...
        
        
    }

} 

有许多不同种类的 CALayer, 但我选择使用 CATextLayer,以便转换在视觉上更清晰。

翻译

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

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

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

示例

enter image description here

在iOS中,坐标系的原点位于左上角,因此如果我们想将图层向右移动90个点并向下移动50个点,我们可以执行以下操作:
myLayer.transform = CATransform3DMakeTranslation(90, 50, 0)

注意事项

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

缩放

缩放变换会拉伸或压缩图层。基本语法如下:

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

说明

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

示例

enter image description here

如果我们想把宽度减半并将高度三倍化,我们将执行以下操作。
myLayer.transform = CATransform3DMakeScale(0.5, 3.0, 1.0)

注意事项

  • 由于我们仅在二维平面内工作,因此只需将 z 坐标乘以 1.0 以使其不受影响。
  • 上图中的红点表示锚点。请注意缩放是相对于锚点进行的。也就是说,所有内容都是向锚点伸展或远离锚点。

旋转

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

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

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

示例

enter image description here

如果我们想要将一个图层顺时针旋转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,但它们将以前一个转换作为参数。

示例

enter image description here

这次我们将前面三个变换结合起来。

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),而位置则表示为超级图层坐标系统中的单位。可以像这样设置它们:

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

如果您只设置锚点而不改变位置,则框架会更改,以便位置位于正确的位置。更准确地说,根据新的锚点和旧的位置重新计算框架。这通常会产生意想不到的结果。以下两篇文章对此进行了深入讨论。

另请参阅


3
很棒的解释,对实施此操作非常有用。如何在调整图层大小时修复文字缩放/扩展问题? - Vijay
@Vijay,我不太确定你的意思。最好为此提出一个新问题。 - Suragch
嗨@Suragch,你知道为什么缩放CALayer对象时,它的中心会移动吗?它应该保持不变。 - Shamsiddin Saidov
@Shamsiddin,我已经有一段时间没有在这上面工作了,但据我所知,中心点确实保持不变。就像上面的缩放示例一样。 - Suragch
@Suragch,你在旋转状态下做什么? - ironRoei
以上的“关于anchorPoint”和“Translate rotate translate”链接已经过时。 - jsbox

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接