在iOS中我們會經常遇到frame和bounds,這兩個概念很相似,但是也有區別。frame還好理解,但是bounds就比較容易迷惑人。我們通過實例來講解下bounds的概念,然后再看看bounds有哪些用途,這樣就可以徹底搞清楚bounds了。
frame和bounds簡介
先看一張圖:
- frame: 該view在父view坐標系統中的位置和大小。(參照點是,父親的坐標系統)
- bounds:該view在本地坐標系統中的位置和大小。(參照點是,本地坐標系統,就相當于ViewB自己的坐標系統,以0,0點為起點)。
其實本地坐標系統的關鍵就是要知道的它的原點(0,0)在父坐標系統中的什么位置(這個位置是相對于父view的本地坐標系統而言的,最終的父view就是UIWindow,它的本地坐標系統原點就是屏幕的左上角了)。
通過修改view的bounds屬性可以修改本地坐標系統的原點位置。
frame我相信大家都理解的比較清楚,但是bounds光是這么說估計大家都很迷糊,那么我們下面來看具體的實例。
bounds到底起什么作用
示例代碼:
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
view1.backgroundColor = [UIColor redColor];
[self.view addSubview:view1];//添加到self.view
NSLog(@"view1 frame:%@========view1 bounds:%@",NSStringFromCGRect(view1.frame),NSStringFromCGRect(view1.bounds));
UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
view2.backgroundColor = [UIColor yellowColor];
[view1 addSubview:view2];//添加到view1上,[此時view1坐標系左上角起點為(-20,-20)]
NSLog(@"view2 frame:%@========view2 bounds:%@",NSStringFromCGRect(view2.frame),NSStringFromCGRect(view2.bounds));
效果圖:
輸出日志:
view1 frame:{{100, 100}, {200, 200}}========view1 bounds:{{0, 0}, {200, 200}}
view2 frame:{{0, 0}, {100, 100}}========view2 bounds:{{0, 0}, {100, 100}}
這個是常規的場景,我相信大家都能理解。
下面我們來改變view1的bounds,代碼如下
[view1 setBounds:CGRectMake(-20, -20, 200, 200)];
此時顯示和輸出日志如下所示:
view1 frame:{{100, 100}, {200, 200}}========view1 bounds:{{-20, -20}, {200, 200}}
view2 frame:{{0, 0}, {100, 100}}========view2 bounds:{{0, 0}, {100, 100}}
分析
上面設置view1的bounds的代碼起到了讓view2的位置改變的作用。為何(-20,-20)的偏移量,卻可以讓view2向右下角移動呢?
這是因為setBounds的作用是:強制將自己(view1)本地坐標系的原點改為(-20,-20)。這個(-20,-20)是相對view1的父view(self.view)偏移的。也就是向左上角偏移。
那么在view1的坐標系中(0,0)這個點是需要向右下各偏移20。
因為view1的subview(view2)的frame參照的坐標系是父view(view1)的bounds設置的,而此時view2的frame設置為(0,0),就會導致view2向右下各偏移20。如上圖所示。
總結
所以,bounds的有這么一個特點:
它是參考自己坐標系,它可以修改自己坐標系的原點位置,進而影響到“子view”的顯示位置。
bounds使用場景
其實bounds我們一直在使用,就是我們使用scrollview的時候。
為什么我們滾動scrollview可以看到超出顯示屏的內容。就是因為scrollview在不斷改變自己的bounds,從而改變scrollview上的子view的frame,讓他們的frame始終在最頂級view(window)的frame內部,這樣我們就可以始終看到內容了。
下面通過一個具體的例子來看看:
self.imageview = [[UIImageView alloc]initWithFrame:CGRectMake(100,0, 50, 1000)];
self.imageview.image = [UIImage imageNamed:@"1"];
self.imageview.contentMode = UIViewContentModeScaleAspectFill;
self.scrollview.contentSize = self.imageview.frame.size;
[self.scrollview addSubview:self.imageview];
在向上滾動過程中,輸出scrollview的frame,bouns,contentoffset和子控件imageview的frame,bounds
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
NSLog(@"scrollview[contentoffset:%@---frame:%@------bounds:%@",NSStringFromCGPoint(scrollView.contentOffset), NSStringFromCGRect(self.scrollview.frame),NSStringFromCGRect(self.scrollview.bounds));
NSLog(@"imageview[frame:%@------bounds:%@",NSStringFromCGRect(self.imageview.frame),NSStringFromCGRect(self.imageview.bounds));
}
輸出結果如下:
分析:
可以看到imageview的frame和bounds還有scrollview的frame是沒有改變的。唯一在不斷改變的是scrollview的contentoffset和bounds,而且兩者完全相同。
結合上面我們講的知識,就不難理解為什么scrollview要這么做了。
向上滾動scrollview,我們就不斷增加scrollview的bounds的y值,也就是不斷把scrollview的本地坐標系原點向下偏移(相對于scrollview的父view的坐標系,y值越大,越向下偏移)。那么此時scrollview的子控件的frame設置的(0,0)就是不斷向上偏移
假設某一時刻scrollview的坐標系原點為(0,100),那么scrollview的(0,0)位置就是相對于坐標系原點向上偏移100的距離,設置scrollview的子控件的frame為(0,0),就是設置子控件左上角在scrollview中的(0,0)位置,那么子控件就會向上偏移100,你也就看到scrollview的內容(子控件)向上滾動的效果。
其實我們可以使用文章開始的例子來模式UIScrollview的滾動效果,經過上面的分析我們知道就是通過不斷增加UIScrollview的bounds的Y值,才可以出現滾動效果從而顯示超出屏幕的內容。
那么使用文章開頭的例子,我們可以不斷增加view1的bounds的y值,來看看是不是可以達到同樣的效果:view1不動,view2在不斷向上滾動
代碼如下:
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(100, 200, 200, 100)];
view1.backgroundColor = [UIColor redColor];
[self.view addSubview:view1];//添加到self.view
UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(20, 0, 100, 1000)];
view2.backgroundColor = [UIColor yellowColor];
[view1 addSubview:view2];//添加到view1上,[此時view1坐標系左上角起點為(-20,-20)]
[UIView animateWithDuration:3.0 animations:^{
[view1 setBounds:CGRectMake(0, 1000, 200, 100)];
}];
運行看看,可以發現view1固定不動,view2在不斷向上滾動,此時的view1就相當于UIScrollview,而view2相當于UIScrollview上面顯示的內容,現在明白了嗎?
bouns大于frame的情況
假設設置了控件的bounds大于frame,那么此時會導致frame被撐大,frame的x,y,width,height都會改變。
結論
- 新的frame的size等于bound的size。
- 新的frame.x = 舊frame.x - (bounds.size.witdh - 舊frame.size.width)/2
- 新的frame.y = 舊frame.y - (bounds.size.height - 舊frame.size.height)/2
bound的改變會累加
假設view1上面添加了view2,view2上面添加了view3。三個view的size都是(100,100)。
我們設置如下:
view1.bound = (0,100,100,100)
view2.bound = (0,100,100,100)
那么此時view3.frame = (0,0,100,100),view3會相對于原來沒有設置view1、view2的bound時的位置向上偏移200。
總結
- frame是參考父view的坐標系來設置自己左上角的位置。
- 設置bounds可以修改自己坐標系的原點位置,進而影響到其“子view”的顯示位置。