iOS開(kāi)發(fā)——frame和bounds詳解
來(lái)源:程序員人生 發(fā)布時(shí)間:2016-07-01 15:25:08 閱讀次數(shù):3392次
在iOS的UI開(kāi)發(fā)中,frame和bounds是兩個(gè)非常容易弄混的概念,而很多開(kāi)發(fā)者在實(shí)際項(xiàng)目中也很少去辨別,因此會(huì)致使出現(xiàn)1些意想不到的問(wèn)題。本篇博客以實(shí)際代碼的方式來(lái)學(xué)習(xí)frame和bounds的使用。相干示例代碼上傳至 https://github.com/chenyufeng1991/FrameAndBounds ,歡迎大家下載查看。
(1)先來(lái)查看1個(gè)界面中的容器self.view的frame和bounds的打印結(jié)果:下面的運(yùn)行結(jié)果都在iPhone5s摹擬器下進(jìn)行。
NSLog(@"self.view.frame = %@",NSStringFromCGRect(self.view.frame));
NSLog(@"self.view.bounds = %@",NSStringFromCGRect(self.view.bounds));
。
在這里我們可以看到,self.view的frame和bounds是1樣的。原點(diǎn)都是在左上角。長(zhǎng)寬正好是全部屏幕的寬高。
(2)frame和bounds難道都是1樣的嗎?固然不是。現(xiàn)在我們對(duì)1個(gè)View做1個(gè)旋轉(zhuǎn)動(dòng)畫。
UIView *view01 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 50, 50)];
view01.backgroundColor = [UIColor grayColor];
[self.view addSubview:view01];
NSLog(@"view01.frame = %@",NSStringFromCGRect(view01.frame));
NSLog(@"view01.bounds = %@",NSStringFromCGRect(view01.bounds));
[UIView transitionWithView:view01 duration:2 options:0 animations:^{
view01.transform = CGAffineTransformMakeRotation(M_PI_4);
} completion:^(BOOL finished) {
if (finished)
{
NSLog(@"view01.frame = %@",NSStringFromCGRect(view01.frame));
NSLog(@"view01.bounds = %@",NSStringFromCGRect(view01.bounds));
}
}];
動(dòng)畫效果以下:

。
我們讓1個(gè)正方形旋轉(zhuǎn)90度,在動(dòng)畫開(kāi)始前和結(jié)束后分別打印frame和bounds,打印結(jié)果以下:
。
此時(shí)可以看到,在動(dòng)畫開(kāi)始前,frame和bounds也變得不1樣了。在旋轉(zhuǎn)動(dòng)畫后,frame產(chǎn)生改變,bounds仍然沒(méi)變。我現(xiàn)在告知大家下面的結(jié)論:
-- frame的位置是根據(jù)父容器來(lái)計(jì)算的,正方形在動(dòng)畫開(kāi)始前的x=100,y=100是相對(duì)self.view的坐標(biāo)系統(tǒng)而言的,從而肯定當(dāng)前視圖在父視圖中的位置。
-- bounds的x,y是根據(jù)自己的坐標(biāo)系統(tǒng)而言的。沒(méi)錯(cuò),每一個(gè)view都有自己的坐標(biāo)系。以自己左上角點(diǎn)為坐標(biāo)原點(diǎn)。所以bounds的x,y默許為(0,0),除非調(diào)用setBounds方法;
-- frame的size不1定等于bounds的size,在旋轉(zhuǎn)后它們的size就不1樣了。
在旋轉(zhuǎn)前后,frame產(chǎn)生了較大的變化,其實(shí)旋轉(zhuǎn)后的frame變成了以下:
旋轉(zhuǎn)后:
。
旋轉(zhuǎn)前:
。
旋轉(zhuǎn)后,左上角origin的x,y產(chǎn)生了改變,size的height,width也 產(chǎn)生了改變,所以frame也就改變了。我們可以把frame理解為所占區(qū)域,其實(shí)旋轉(zhuǎn)后的占用區(qū)域是產(chǎn)生改變的。但是為何bounds沒(méi)有改變呢?
View在旋轉(zhuǎn)進(jìn)程中,其實(shí)自己的坐標(biāo)系統(tǒng)并沒(méi)有產(chǎn)生改變,bounds中的origin只能通過(guò)setBounds方法修改。 根據(jù)英文中的意思,bounds就是邊界的意思,在旋轉(zhuǎn)進(jìn)程中,View的邊界長(zhǎng)短并沒(méi)有產(chǎn)生改變,所以bounds的size也就沒(méi)有改變。
所以我做個(gè)小小的別名:把frame理解為占用區(qū)域,把bounds理解為邊界。
(3)我們把1個(gè)子View放到父View中,并且改變父View的bounds,來(lái)看看會(huì)產(chǎn)生甚么?
UIView *view02 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
view02.backgroundColor = [UIColor colorWithWhite:0.851 alpha:1.000];
[self.view addSubview:view02];
UIView *view02_sub = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];
view02_sub.backgroundColor = [UIColor colorWithRed:1.000 green:0.908 blue:0.552 alpha:1.000];
[view02 addSubview:view02_sub];
[self printFrameAndBounds:view02 viewOfSub:view02_sub];
[UIView animateWithDuration:1 animations:^{
// setBounds 強(qiáng)迫將自己坐標(biāo)系的左上角點(diǎn)改成(⑶0,⑶0)。那末真實(shí)的原點(diǎn)(0,0)自然向右下角偏移(30,30);
// 注意:setBounds中的(x,y)只改變自己的坐標(biāo)系統(tǒng)。子view的bounds和frame其實(shí)不會(huì)改變。
[view02 setBounds:CGRectMake(⑶0, ⑶0, 200, 200)];
} completion:^(BOOL finished) {
[self printFrameAndBounds:view02 viewOfSub:view02_sub];
}];
打印結(jié)果以下:
。
運(yùn)行效果圖以下:
。
我們通過(guò)setBounds把父View的origin=(0,0)改成了(⑶0,⑶0), 發(fā)現(xiàn)子View向右下方產(chǎn)生了移動(dòng)。我們來(lái)分析1下緣由。
我們剛剛提到,setBounds可以改變自己View的坐標(biāo)系,父View強(qiáng)迫把自己左上角的原點(diǎn)(0,0)坐標(biāo)改成了(⑶0,⑶0),但是對(duì)子View.frame.origin=(0,0)來(lái)講,它的x,y沒(méi)有產(chǎn)生改變,依然是(0,0), 由于左上角已改成(⑶0,⑶0),所以真實(shí)的原點(diǎn)(0,0)向右下方移動(dòng),所以子View也就向右下方移動(dòng)了。
做1個(gè)小小的總結(jié):
-- setBounds中的(x,y)只改變自己的坐標(biāo)系統(tǒng),子View的bounds和frame其實(shí)不會(huì)改變;
-- setBounds是修改自己坐標(biāo)系的原點(diǎn)位置,進(jìn)而影響到子View的顯示位置;
-- bounds改變位置時(shí),改變的是子視圖的位置,本身沒(méi)有影響,其實(shí)就是改變了本身的坐標(biāo)系原點(diǎn),默許原點(diǎn)在左上角。
(4)當(dāng)父View的frame改變的時(shí)候,子View會(huì)產(chǎn)生甚么變化?
UIView *view02 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
view02.backgroundColor = [UIColor colorWithWhite:0.851 alpha:1.000];
[self.view addSubview:view02];
UIView *view02_sub = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];
view02_sub.backgroundColor = [UIColor colorWithRed:1.000 green:0.908 blue:0.552 alpha:1.000];
[view02 addSubview:view02_sub];
[self printFrameAndBounds:view02 viewOfSub:view02_sub];
[UIView animateWithDuration:1 animations:^{
[view02 setFrame:CGRectMake(0, 250, 150, 150)];
} completion:^(BOOL finished) {
[self printFrameAndBounds:view02 viewOfSub:view02_sub];
}];
打印結(jié)果以下:
。
運(yùn)行效果動(dòng)畫:
。
從效果圖上可以看到,我們改變了父View的位置和大小(坐標(biāo)系原點(diǎn)依然是(0,0),但是實(shí)際位置已改變了,坐標(biāo)系改變),子View的位置也改變了。但是子View的frame和bounds并沒(méi)有改變。由于子View.origin是相對(duì)父View的而言的,這并沒(méi)有改變。
(5)我們上述都只改變了bounds的位置,而沒(méi)有改變bounds的大小,我們來(lái)看看改變bounds的大小會(huì)產(chǎn)生甚么?
UIView *view02 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
view02.backgroundColor = [UIColor colorWithWhite:0.851 alpha:1.000];
[self.view addSubview:view02];
UIView *view02_sub = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];
view02_sub.backgroundColor = [UIColor colorWithRed:1.000 green:0.908 blue:0.552 alpha:1.000];
[view02 addSubview:view02_sub];
[self printFrameAndBounds:view02 viewOfSub:view02_sub];
[UIView animateWithDuration:1 animations:^{
[view02 setBounds:CGRectMake(0, 0, 200, 200)];
} completion:^(BOOL finished) {
[self printFrameAndBounds:view02 viewOfSub:view02_sub];
}];
打印結(jié)果以下:
。
運(yùn)行動(dòng)畫效果以下:
。
我們使用setBounds方法增大了父View的bounds.size ,可以看到把frame也改變了。所以我做1個(gè)小小的總結(jié):
-- 更改bounds的大小,bounds的大小代表當(dāng)前視圖的長(zhǎng)和寬,修改長(zhǎng)寬后,中心點(diǎn)繼續(xù)保持不變,長(zhǎng)寬進(jìn)行改變,通過(guò)bounds修改長(zhǎng)寬就像是以中心點(diǎn)為基準(zhǔn)點(diǎn)對(duì)長(zhǎng)寬兩邊同時(shí)進(jìn)行縮放。
-- center是根據(jù)父容器的相對(duì)位置來(lái)計(jì)算的,不管是修改父容器的bounds還是本身的bounds,都不會(huì)改變center。況且使用bounds來(lái)縮放view,都是根據(jù)center中心點(diǎn)來(lái)縮放的,所以center不會(huì)改變。
-- setBounds也能夠修改view的大小,進(jìn)而修改frame。
(6)除有setBounds方法,一樣有setFrame方法,我們來(lái)看看使用setFrame改變View的大小會(huì)產(chǎn)生甚么?
UIView *viewFather = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 200, 200)];
viewFather.backgroundColor = [UIColor colorWithWhite:0.741 alpha:1.000];
[self.view addSubview:viewFather];
UIView *viewSub = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 50, 80)];
viewSub.backgroundColor = [UIColor colorWithRed:1.000 green:0.999 blue:0.721 alpha:1.000];
[viewFather addSubview:viewSub];
// (6)修改父視圖的frame的大小。父容器的center改變,子視圖的center不變。
[self printFrameAndBounds:viewFather viewOfSub:viewSub];
[UIView animateWithDuration:3 animations:^{
[viewFather setFrame:CGRectMake(50, 50, 250, 250)];
} completion:^(BOOL finished) {
[self printFrameAndBounds:viewFather viewOfSub:viewSub];
}];
打印結(jié)果以下:
。
動(dòng)畫效果以下:
。
仔細(xì)視察可以看到,setFrame改變大小和setBounds改變大小是完全不1樣的,setFrame改變長(zhǎng)寬是從左上角原點(diǎn)進(jìn)行縮放的,固定的是原點(diǎn)。而setBounds則固定的是center。
1句話說(shuō):使用frame改變view大小,center改變,由于縮放參考點(diǎn)為左上角。使用bounds改變view大小,center不變,由于縮放參考點(diǎn)為center。
(7)為了上面打印frame,bounds,centre的方便,上面觸及打印父子View方法調(diào)用的printFrameAndBounds方法實(shí)現(xiàn)以下:
- (void)printFrameAndBounds:(UIView *)viewOfFather viewOfSub:(UIView *)viewOfSub
{
NSLog(@"viewOfFather.frame = %@;viewOfFather.bounds = %@;viewOfFather.center = %@",NSStringFromCGRect(viewOfFather.frame),NSStringFromCGRect(viewOfFather.bounds),NSStringFromCGPoint(viewOfFather.center));
NSLog(@"viewOfSub.frame = %@;viewOfSub.bounds = %@;viewOfSub.center = %@",NSStringFromCGRect(viewOfSub.frame),NSStringFromCGRect(viewOfSub.bounds),NSStringFromCGPoint(viewOfSub.center));
}
個(gè)人總結(jié)了1下:
-- 如果想修改view的位置而不影響其他,修改本身frame的位置;想修改view的大小,修改frame的大小或bounds的大小(斟酌相對(duì)位置的改變)。
-- 如果想修改view的所有子view的位置,修改view的bounds的位置(父容器坐標(biāo)系)。
個(gè)人建議,想要查看某個(gè)變量的改變效果,可使用我們初高中實(shí)驗(yàn)中的“單1變量原則”,也就是1次代碼中只改變1個(gè)變量來(lái)運(yùn)行,這樣就可以很方便的知道某個(gè)變量的作用了。具體可以參考Github中的demo。
生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)