这里只讨论平行坐标系,不涉及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不想等的话)