草庐IT

ios - 如何在 CALayer 上进行转换?

coder 2023-07-14 原文

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

  • 有过 Affine transforms for views 的经验
  • 阅读 Transforms documentation在 Quartz 2D 编程指南
  • 看过 this detailed CALayer tutorial
  • 下载并运行 LayerPlayer project来自 Github

  • 但是,我仍然无法理解如何对图层进行基本变换。寻找平移、旋转和缩放的解释和简单示例一直很困难。

    今天我终于决定坐下来,做一个测试项目,然后弄清楚。我的回答如下。

    笔记:
  • 我只做 Swift,但如果其他人想添加 Objective-C 代码,请成为我的客人。
  • 在这一点上,我只关心理解 2D 变换。
  • 最佳答案

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

  • 翻译(移动)
  • 缩放
  • 旋转


  • 要对 CALayer 进行转换,请将图层的 transform 属性设置为 CATransform3D 类型。例如,要翻译图层,您可以执行以下操作:
    myLayer.transform = CATransform3DMakeTranslation(20, 30, 0)
    
    单词 Make 用于创建初始变换的名称:CATransform3D Make Translation。应用的后续转换省略 Make 。例如,请参阅此旋转后跟平移:
    let rotation = CATransform3DMakeRotation(CGFloat.pi * 30.0 / 180.0, 20, 20, 0)
    myLayer.transform = CATransform3DTranslate(rotation, 20, 30, 0)
    
    现在我们已经了解了如何进行转换,让我们看一些如何进行每个转换的示例。不过,首先,我将展示我如何设置该项目,以防您也想尝试一下。
    设置
    对于以下示例,我设置了一个单一 View 应用程序,并向 Storyboard添加了一个浅蓝色背景的 UIView。我使用以下代码将 View 连接到 View Controller :
    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
  • 上图中的红线从原始位置的中心到新位置的中心。这是因为转换是相对于 anchor 完成的,并且默认情况下 anchor 位于图层的中心。

  • 规模
    缩放变换会拉伸(stretch)或挤压图层。基本语法是
    CATransform3DMakeScale(_ sx: CGFloat, _ sy: CGFloat, _ sz: CGFloat)
    
    其中 sxsysz 分别是用于缩放(乘以)x、y 和 z 坐标的数字。
    示例

    如果我们想将宽度减半,高度增加三倍,我们将执行以下操作
    myLayer.transform = CATransform3DMakeScale(0.5, 3.0, 1.0)
    
    笔记
  • 由于我们只在二维中工作,我们只是将 z 坐标乘以 1.0 以保持它们不受影响。
  • 上图中的红点代表 anchor 。注意缩放是如何相对于 anchor 完成的。也就是说,一切都朝着或远离 anchor 拉伸(stretch)。

  • 旋转
    旋转变换围绕 anchor (默认为图层的中心)旋转图层。基本语法是
    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 来逆时针旋转。
  • 红点表示 anchor 的位置。旋转是围绕 anchor 完成的。

  • 多次变换
    为了组合多个变换,我们可以像这样使用连接
    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
    
    笔记
  • 转换的顺序很重要。
  • 一切都与 anchor (红点)有关。

  • 关于 anchor 和位置的说明
    我们在不改变 anchor 的情况下完成了上面的所有变换。但是,有时需要更改它,例如如果您想围绕除中心之外的其他点旋转。但是,这可能有点棘手。
    anchor 和位置都在同一个地方。 anchor 以图层坐标系的单位表示(默认为 0.5, 0.5 ),位置以上层坐标系表示。他们可以这样设置
    myLayer.anchorPoint = CGPoint(x: 0.0, y: 1.0)
    myLayer.position = CGPoint(x: 50, y: 50)
    
    如果您只设置 anchor 而不更改位置,则框架会更改,以便位置位于正确的位置。或者更准确地说,框架是根据新的 anchor 和旧位置重新计算的。这通常会产生意想不到的结果。以下两篇文章对此进行了很好的讨论。
  • About the anchorPoint
  • Translate rotate translate?

  • 也可以看看
  • Border, rounded corners, and shadow on a CALayer
  • Using a border with a Bezier path for a layer
  • 关于ios - 如何在 CALayer 上进行转换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34438889/

    有关ios - 如何在 CALayer 上进行转换?的更多相关文章

    1. ruby - 如何在 Ruby 中顺序创建 PI - 2

      出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

    2. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

      我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

    3. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

      我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

    4. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

      如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

    5. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

      我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

    6. ruby - 将数组的内容转换为 int - 2

      我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]

    7. ruby - 将散列转换为嵌套散列 - 2

      这道题是thisquestion的逆题.给定一个散列,每个键都有一个数组,例如{[:a,:b,:c]=>1,[:a,:b,:d]=>2,[:a,:e]=>3,[:f]=>4,}将其转换为嵌套哈希的最佳方法是什么{:a=>{:b=>{:c=>1,:d=>2},:e=>3,},:f=>4,} 最佳答案 这是一个迭代的解决方案,递归的解决方案留给读者作为练习:defconvert(h={})ret={}h.eachdo|k,v|node=retk[0..-2].each{|x|node[x]||={};node=node[x]}node[

    8. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

      exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

    9. ruby - 如何在续集中重新加载表模式? - 2

      鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

    10. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

      我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

    随机推荐