CGAffineTransform链式调用的问题

本文通过示例探讨了CGAffineTransform中translate、scale和rotate链式调用的顺序对视图位置和大小的影响。发现变换顺序不同会导致最终效果的差异,例如缩放会影响后续的所有移动操作,而旋转会对后续的平移产生变化。这些发现对于精确控制视图变换至关重要。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Demo 地址

今天使用CGAffineTransform遇到一个问题,可能是坑,在这说明下:

CGAffineTransform 提供了3个Api给大家用:

移动:(A)

public func translatedBy(x tx:CGFloat, y ty:CGFloat) ->CGAffineTransform

缩放:(B)

public func scaledBy(x sx:CGFloat, y sy:CGFloat) ->CGAffineTransform

旋转:  (C)

public func rotated(by angle:CGFloat) ->CGAffineTransform


swift Api提供了返回值CGAffineTransform,我们可以链式调用,很方便 view.A.B.C  (A B C 代表上面的3个Api)


现在问题来了:

1. view.A.A.A.A.A.A  或  view.B.B.B.B.B 或  view.C.C.C.C.C.C   方式多次调用,移来移去,缩放缩放,转来转去嘛,很ok

2. view.A.B.C  view.B.A.C   view.C.A.B   效果会是一样的吗?  答案是NO

3. view.A.A.A.B.B.C   或  view.A.B.C.A.B.C 

4. view.[A.A.A.B.B.C] 或 view.[A.B.C.A.B.C]  此种方式不采用链式调用,而是将每次的改变值都累加到一个总的值里,执行一个总值的transform,效果会和3一样吗? 答案是NO


具体效果可以看我的Demo里 trans3  trans4trans5 trans6 trans7 trans8



看几个例子:

1. 

self.firstView.transform =self.firstView.transform.scaledBy(x:0.2, y: 0.2).translatedBy(x:100.0, y: 100.0)

self.firstView.transform = self.firstView.transform.translatedBy(x:100.0, y: 100.0).scaledBy(x:0.2, y: 0.2)


效果一样吗? 答案是NO,frame的大小是一致的,但是center是不一样的。

方式一: center:157.0,336.833333333333 -> center:177.0,356.833333333333  实际移动距离为 0.2*100 = 20

方式二:center:157.0,336.833333333333 -> center:257.0,436.833333333333  实际移动距离为100


有点意思:先缩后移的话,移动的距离被缩放了;先移后缩则没问题


2. 

self.firstView.transform =self.firstView.transform.scaledBy(x:0.5, y: 0.5).translatedBy(x:100.0, y: 100.0).scaledBy(x:0.4, y: 0.4)

self.firstView.transform = self.firstView.transform.scaledBy(x:0.2, y: 0.2).translatedBy(x:100.0, y: 100.0)


效果会一样吗? 答案是NO,frame的大小是一致的,但是center是不一样的。

方式一:center:157.0,336.833333333333 -> center:207.0,386.833333333333   实际移动距离为 0.5*100 = 50

方式二:center:157.0,336.833333333333 -> center:177.0,356.833333333333   实际移动距离为 0.2*100 = 20


有点意思:结合实例1的方式二和实例2的方式一,看的出缩放不会对它前面的移动有改变,只会改变它之后的移动,那是仅对下一个移动起作用,还是对以后所有的移动都起作用呢?  我们来看实例3


3. 

self.firstView.transform =self.firstView.transform.scaledBy(x:0.5, y: 0.5).translatedBy(x:100.0, y: 100.0).scaledBy(x:0.4, y:0.4).translatedBy(x:100.0, y: 100.0)

self.firstView.transform =self.firstView.transform.scaledBy(x:0.2, y: 0.2).translatedBy(x:200.0, y: 200.0)


效果会一样吗? 答案是NO,frame的大小是一致的,但是center是不一样的。

方式一:center:157.0,336.833333333333 -> center:227.0,406.833333333333   实际移动距离为  0.5*(100 + 0.4*100) = 70

方式一:center:157.0,336.833333333333 -> center:197.0,376.833333333333   实际移动距离为  0.2*200 = 40

OK,上面的问题有了答案:缩放会对它右侧所有的移动进行改变。




4. 

self.firstView.transform =self.firstView.transform.scaledBy(x:0.5, y: 0.5).translatedBy(x:100.0, y: 100.0).scaledBy(x:0.4, y:0.4).translatedBy(x:100.0, y: 100.0).scaledBy(x:0.5, y: 0.5).translatedBy(x:100.0, y: 100.0)

self.firstView.transform =self.firstView.transform.scaledBy(x:0.1, y: 0.1).translatedBy(x:300.0, y: 300.0)


效果会一样吗? 答案是NO,frame的大小是一致的,但是center是不一样的。

方式一:center:157.0,336.833333333333 -> center:237.0,416.833333333333  实际移动距离为  0.5*(100 + 0.4* (100 + 0.5*100)) = 80

方式二:center:157.0,336.833333333333 -> center:187.0,366.833333333333  实际移动距离为  0.1*300 = 60


OK 进一步验证了实例3的结论。


此处可以得出一个公式:实际移动距离 = B* (A + B*(A + B*( ...)))  


有了这个结论,你应该知道怎么来使用transform达到预期的效果了吧,不会被移的一头雾水了。。


我开始的预期是移动和缩放是相互独立的,都是以Frame的center进行的,不管是先移后缩,还是先缩后移,效果应该是一样的,结果呢,呵呵,被打脸了

先控制个笑脸在屏幕上游荡的话,还是自己累积变化量吧,否则都不知道会飘到哪去。。 具体看Demo里的  trans5



继续更新:

上面只涉及到移动和缩放功能,还没有涉及到旋转。接着看实例

5. 

self.firstView.transform = self.firstView.transform.rotated(by: CGFloat(Float.pi/3)).translatedBy(x: 100.0, y: 100.0)

self.firstView.transform = self.firstView.transform.translatedBy(x: 100.0, y: 100.0).rotated(by: CGFloat(Float.pi/3))


效果会一样吗? 答案是NO,frame的大小是一致的,但是center是不一样的。

方式一:center:157.0,336.833333333333 -> center:120.397471925123,473.435877008508  实际移动距离为 f(Float.pi/3)*100  f的具体内容不再细究,有兴趣可以研究下

方式二:center:157.0,336.833333333333 ->  center:257.0,436.833333333333  实际移动距离为 100


可以看得出来:先移后转没有影响,先旋转后平移的话,旋转会对后续的平移产生变化,应该也是对后续所有的平移起作用。

有意思的一个点:不管是方式一还是方式二,执行 2/(1/3) = 6次后,view就回到了原点,赚了一圈回来了。 6次是怎么算出来的呢?一圈是2*pi,每次旋转pi/3,所以是6次喽。平移的距离越大,转的圈就越大,从上面的公式也可以看的出来。



今天先到这吧



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值