1.三个属性的基本介绍:
(1)frame:描述当前视图在其父视图中的位置和大小。
(2)bounds:描述当前视图在其自身坐标系统中的位置和大小。
(3)center:描述当前视图的中心点在其父视图中的位置。
2.必须明确的几个逻辑原点:
1.位置是相对的,想要表示一个物体的位置必须有参照系。(不同的参照系对相同位置的表示有什么不同?)
2.控件的原始坐标系:默认控件的左上角为原点,值{0,0},原点出发水平向右是x轴正方向,递增;原点出发垂直向下是y轴正方向,递增。
3.控件的bounds.origin(最初值都是{0,0})和frame.origin代表的都是控件左上角点的坐标。可以修改。(如何修改,会有什么影响?)
4.bounds.size和frame.size都表示控件的大小,永远一样。(size的改变是如何影响bounds和frame的?)
5.bounds代表本地(控件自己的)坐标系,frame代表父控件坐标系。坐标必须在同一个坐标系中才能比较。
3.探索bounds和frame的区别和联系
一般大家对frame的改变和影响比较熟悉,但是对bounds改变比较陌生。所以,下面就对父控件、子控件的bounds.origin和bounds.size分为四种情况进行组合变换来探究。通过setBounds方法来改变bounds值。
- (void)viewDidLoad {
[super viewDidLoad];
//添加父控件orangeView
UIView * orangeView = [[UIView alloc]initWithFrame:CGRectMake(100, 100, 200, 200)];
orangeView.backgroundColor = [UIColor orangeColor];
[self.view addSubview:orangeView];
//添加子控件yellowView
UIView * yellowView = [[UIView alloc]initWithFrame:CGRectMake(20, 20, 100, 100)];
yellowView.backgroundColor = [UIColor yellowColor];
[orangeView addSubview:yellowView];
}
输出bounds和frame值:
orangeView: bounds:{{0, 0}, {200, 200}}--frame:{{100, 100}, {200, 200}};
yellowView: bounds:{{0, 0}, {100, 100}}--frame:{{20, 20}, {100, 100}}。
3.1 保持bounds.size不变,只改变bounds.origin
3.1.1 父控件:
[orangeView setBounds:CGRectMake(-30, -30, 200, 200)];
输出值:
orangeView: bounds:{{-30, -30}, {200, 200}}--frame:{{100, 100}, {200, 200}};
yellowView: bounds:{{0, 0}, {100, 100}}--frame:{{20, 20}, {100, 100}}。
从输出结果可以得出:父控件的bounds值变为修改后的值,frame不变;子控件bounds值不变(意料之中),值得注意的是,虽然子控件的位置移动了(相对于屏幕,示例程序是向右下方移动;相对于父控件的话,其实子控件位置不变,因为frame不变),但是子控件的frame不变。(父控件通过改变自身的bounds.origin值(自身坐标体系)抵消了子控件位移造成的frame.origin的改变(子控件的frame和父控件的bounds在同一个坐标体系中,因此可以互相抵消))
3.1.2 子控件:
[yellowView setBounds:CGRectMake(-30, -30, 100, 100)];
输出值:
orangeView: bounds:{{0, 0}, {200, 200}}--frame:{{100, 100}, {200, 200}};
yellowView: bounds:{{-30, -30}, {100, 100}}--frame:{{20, 20}, {100, 100}}。
从输出结果可以得出:父控件的frame和bounds值都不变,子控件只有bounds变为修改后的值,frame值不变,子控件位置也不变。
小结:从3.1.1和3.1.2的情况可以分析出:只要bounds值的origin被修改,size值不变,此时无论父控件还是子控件,只有被修改的bounds值会变为修改值,其他的值不变。但要注意:修改父控件的bounds值的origin值会导致子控件的位置改变(相对于屏幕)。
3.2 保持bounds.origin不变,只改变bounds.size
3.2.1 父控件
[orangeView setBounds:CGRectMake(0, 0, 250, 250)];
输出值:
orangeView: bounds:{{0, 0}, {250, 250}}--frame:{{75, 75}, {250, 250}};
yellowView: bounds:{{0, 0}, {100, 100}}--frame:{{20, 20}, {100, 100}}。
从输出结果可以得出:父控件的bounds值变为修改值,frame值发生改变;子控件的bounds和frame不变。父控件的图形变换规律是:以变换前的center为中心,左右、上下等距离扩大或缩小。因此,假设原子父控件的frame = {{x, y}, {w, h}},修改后的父控件bounds = {{0, 0}, {w1, h1}},那么变换后的frame = {{x-(w1-w)/2, y-(h1-h)/2}, {w1, h1}};子控件的位置(相对于父控件)不变,因为frame没变。(图中的绿色控件是父控件原来的位置,作为对照)
3.2.2 子控件
[yellowView setBounds:CGRectMake(0, 0, 250, 250)];
输出值:
bounds:{{0, 0}, {200, 200}}--frame:{{100, 100}, {200, 200}};
yellowView: bounds:{{0, 0}, {250, 250}}--frame:{{-55, -55}, {250, 250}}
从输出结果可以得出:子控件的bounds值变为修改值,frame值发生改变;父控件的bounds和frame不变。子控件的图形变换规律和3.2.1中子控件一样;子控件位置(相对于父控件)改变,因为frame变了。
小结:从3.2.1和3.2.2的情况可以分析出::只要bounds值的size被修改,origin值不变,此时无论父控件还是子控件,被修改控件的bounds值变为修改值,frame值也会改变(计算公式如上),其他的值不变。
3.3 同时改变bounds.origin和bounds.size
由3.2和3.3分析可以推测出结果(已验证):
1.父控件bounds和frame改变,子控件位置改变(相对于屏幕)。
2.和3.2.2的到的结果相同。
小结:bounds.size的改变要优先于bounds.origin改变进行处理。
综述:
1.bounds.size改变时,当前控件的frame值改变,bounds值变为改变后的值,但不影响其他控件两个参数值。
2.bounds.origin改变时,当前控件的frame值不变,bounds值变为改变后的值,但不影响其他控件两个参数值。
3.bounds.size的改变要优先于bounds.origin改变进行处理。
4.控件的bounds.size或bounds.origin的改变,只要改变了坐标系,在同一个坐标系中的其他的控件的位置(相对于屏幕)也会受到影响。
5.以上规律对于控件是子控件或是父控件无需考虑。
4 应用场景
苹果官方控件UITableView和UICollectionView就有很好的利用综述中第4点的特性来改变cell的移动的。UITableView或UICollectionView相当于父控件,cell相当于子控件,当滑动屏幕的时候,不断地改变tableview或collectionview的bounds.origin,从而使所有的cell相对于屏幕的位置发生变化,产生向上或向下滚动的效果,而cell的frame并没有发生改变,这样就不用在滑动过程中重新计算cell的frame值。
参考文章: