iOS

iOS坐标系

Posted by Maqy on October 8, 2018

这里只讨论平行坐标系,不涉及z轴。

简介

iOS布局是根据坐标系来进行布局的,分为x和y轴,这里有几个重要属性需要提前介绍下。

layer的属性:

anchorPoint: [CGPoint]锚点,用于表示当前点在自己的视图中的百分比位置
position:[CGPoint]用于记录锚点在父坐标系中的位置
bounds:[CGRect]记录当前视图自己的坐标系位置及大小
frame:[CGRect]记录自己在父坐标系中的位置及大小
transform:矩阵坐标系计算相关

下面来介绍下属性之间的关系。

Transform & bounds & frame

应该是最好理解的一个,用于视图的形变。

比如有层级关系如下的视图 A(黑色)->B(红色)->C(蓝色)

坐标系如下:
B frame = CGRect(100, 100, 200, 200);
C frame = CGRect(50, 50, 100, 100);

那么实际上 B 的 frame 就是 CGRect(100, 100, 200, 200), bounds 是 CGRect(0, 0, 200, 200)
C 的 frame 就是 CGRect(50, 50, 100, 100), bounds 是 CGRect(0, 0, 100, 100)

Case 1:

执行 B.transform = CGAffineTransformMakeTranslation(50, 50); // 横纵坐标都位移50

其实际执行结果是将B在A的坐标系内向下向右各移动50个pt,注意这里是整体,也就是包括其子view
那么实际上 B 的 frame 就是 CGRect(150, 150, 200, 200), bounds 是 CGRect(0, 0, 200, 200)
C 的 frame 就是 CGRect(50, 50, 100, 100), bounds 是 CGRect(0, 0, 100, 100)

可以看到,如果只是执行位移的transform,那么在UI上的实际表现就是做了一个位移,且只有view的frame跟着相对变更

Case 2:

执行 B.transform = CGAffineTransformMakeScale(0.5, 2); // 宽度缩窄到一半,高度变成2倍长

其实际执行结果是将B在A的坐标系内向下向右各移动50个pt,注意这里是整体,也就是包括其子view
那么实际上 B 的 frame 就是 CGRect(150, 0, 100, 400), bounds 是 CGRect(0, 0, 200, 200)
C 的 frame 就是 CGRect(50, 50, 100, 100), bounds 是 CGRect(0, 0, 100, 100)

为什么会是上边这个样子呢?

这里先说个概念就是,任何形变或者动画在表现的时候,都是基于一个点的位置来进行计算的,这个点就是锚点,默认的锚点值是CGpoint(0.5, 0.5)

例如位移就是把锚点相对于父坐标系的位置进行位移,大小不变

而这里的放大缩小就是锚点在父坐标系位置不变,但是宽度缩窄,高度变高,所以红色在父坐标系的frame就变成了CGRect(150, 0, 100, 400)

** 但是虽然B的frame变了,但是因为是形变,所以不涉及自己的坐标的的大小的变化,所以还是原来的CGRect(0, 0, 200, 200) **

那为什么C也跟着伸长了呢?

因为C在父坐标系的大小和位置不变,那么B的坐标系拉伸的话,C的百分比不变的话,在表现上也像是被拉伸了

Case 3:

B.transform = CGAffineTransformMakeRotation(M_PI*0.1); // 旋转

这里原理和Case2类似,但如果B的边与A不平行的话,B的frame的大小则计算为B的最大的矩形,就不赘述了

AnchorPoint & position

先总结几句话:

1.改变frame会改变position的值 及 bounds的size,不影响anchorPoint

2.改变bounds会影响frame的size,不影响anchorPoint和position

3.改变position影响frame的origin

4.改变anchorPoint需要重计算frame的origin

5.相同的改变的代码调用顺序不同会产生不同的结果呀

case 1:

改变frame大家都懂就不说了

case 2:

// 假设 anchorPoint 为默认值(中心点)
将一个frame=CGRect(100, 100, 200, 200)的view的bounds改为CGRect(0, 0, 100, 100)
得到view相对于父坐标系的位置并不是CGRect(100, 100, 100, 100)而是CGRect(150, 150, 200, 200)

REASON:这是因为锚点值没变,且position也没变还是CGPoint(200, 200),所以位置也跟着改变了

那如果bounds改为CGRect(100, 100, 100, 100),其实在父识图上的展示不会变,但会影响子view的坐标

case 3:

只改变position的话其实和改变frame的origin一个效果

case 4:

改变anchorPoint的时候,因为position的值没变,但相对于自己的坐标系的百分比变了,所以会触发frame的变化

case 5:

不同的调用顺序,需要根据上面的规则自己计算下

总结

1.一定要注意 anchorPoint position frame bounds 等的调用顺序

2.transform 如果是个循环动画的话,记得不要中途改变frame,因为这个时候的transform的值不固定,而两者又相互作用,所以得到的结果会有问题

3.anchorPoint 和 autoLayout 的相互作用有点奇怪,有兴趣的可以研究下,因为我们不能确定autoLayout内部的调用顺序,所以最好不要一起使用(如果各个视图的anchorPoint不想等的话)