当前位置: 首页 > 面试题库 >

如何在CALayer上进行转换?

袁晟
2023-03-14
问题内容

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

  • 有使用Affine转换的经验
  • 阅读Quartz 2D编程指南中的Transforms文档
  • 看过这个详细的CALayer教程
  • 从Github 下载并运行LayerPlayer项目

但是,我仍然难以理解如何在 图层 上进行基本转换。寻找用于平移,旋转和缩放的解释和简单示例非常困难。

今天,我终于决定坐下来,进行测试项目,然后找出答案。我的答案如下。

笔记:

  • 我只做Swift,但是如果其他人想添加Objective-C代码,请做我的客人。
  • 在这一点上,我只关心了解2D变换。

问题答案:

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

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

在此处输入图片说明

要对进行转换CALayer,您可以将图层的transform属性设置为CATransform3D类型。例如,要翻译图层,您可以执行以下操作:

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

这个词Make是在名称中使用创建初始转换:CATransform3D 翻译。随后应用的转换会忽略Make。例如,参见此轮换后跟翻译:

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

现在我们已经有了进行转换的基础,让我们看一下如何进行转换的一些示例。不过,首先,我将展示如何设置项目,以防您也想玩这个项目。

建立

对于以下示例,我设置了SingleViewApplication,并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)

其中txx坐标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轴旋转。因此,我们将x和设置y0.0并设置z1.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),位置表示为上层坐标系。可以这样设置

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

如果仅设置锚点而不更改位置,则框架会更改,因此位置将在正确的位置。或更准确地说,将根据新的锚点和旧位置重新计算框架。这通常会产生意外的结果。以下两篇文章对此进行了精彩的讨论。

  • 关于anchorPoint
  • 翻译旋转翻译?


 类似资料:
  • 在Android 5.0Lollipop, 我有两个活动A和B。活动B有一个带有覆盖操作栏的滑动输入从下到上的转换,但是当B显示时,操作栏也从下到上滑动。 如何防止actionbar发生幻灯片转换。系统actionbar是否有可以添加到排除目标的id? 谢谢!

  • 问题内容: 如何在Go中反转任意切片()?我宁愿不必编写和使用。有没有简单的内置方法来做到这一点? 问题答案: 没有一个简单的,内置的用于反转接口{}的部分。您可以编写一个for循环来做到这一点: Go 1.8中引入的reflect.Swapper函数可用于编写通用的反转函数: 游乐场的例子

  • 问题内容: 我需要取消转义包含转义的XML标签的xml字符串: 我确实找到了一些可以执行此任务的库,但是我宁愿使用可以执行此任务的单一方法。 有人可以帮忙吗? 干杯,Bas Hendriks 问题答案: (commons-lang,下载)

  • 我在AP计算机科学的9年级,我们得到了一个制作游戏的项目。我选择了老师建议的一个简单的气游戏。但我不知道如何在电脑和播放器之间交替旋转。

  • 问题内容: 我知道JVM参数。我也知道,这将要求堆转储。 问题: 如何确保我首先进行完整的堆转储, 然后 在转储完成后强制重新启动(或终止)?是我最好的选择吗? 问题答案: JVM将首先转储堆,然后执行OnOutOfMemoryError命令(证明)。

  • 问题内容: 我们有一个运行Java 5的长期运行的服务器应用程序,对其进行分析,我们可以看到随着时间的流逝,旧的一代增长缓慢。它已在完整的GC上正确释放,但我希望能够使用堆转储查看Eclipse MAT中无法访问的对象。我已经使用+ XX:HeapDumpOnCtrlBreak成功获得了堆转储,但是JVM总是在转储堆之前执行GC。显然,在Java 6上不会发生这种情况,但目前我们只在5上停留。有什