對於IOS開發者來講,在自動佈局出現前只能經過計算和設置frame的值來處理,這樣設置位置時就會出現不少硬編碼,同時在屏幕旋轉和不一樣屏幕之間適配時須要編碼從新調整位置和尺寸,咱們也能夠重載視圖的layoutSubviews的函數來寫代碼從新佈局。自動佈局出現後確實在必定程度上解決了位置和尺寸硬編碼的問題,可是經過代碼來寫自動佈局很是的複雜和麻煩,並且代碼量會增長不少。在自動佈局領域android系統經過提供FrameLayout, LinearLayout, RelativeLayout, AbsoluteLayout等幾個類來分別處理各類不一樣的佈局需求,經過wrap_content,match_parent來自動計算尺寸。android
android系統的FrameLayout類用於進行上下左右居中填充方式的佈局,而LinearLayout則是用於進行水平和垂直方向的流式佈局,AbsoluteLayout則是硬編碼方式的絕對佈局。在我前面的2篇文章中分別介紹了MyFrameLayout, MyLinearLayout兩種方式的佈局,而這章我將繼續介紹相對佈局MyRelativeLayout.git
所謂相對佈局就是指某個視圖的位置和尺寸不是固定寫死的而是依賴於其餘關聯的視圖,好比一個視圖在另一個視圖的左邊,或者在另一個視圖的右下方,或者一個視圖的寬度和另一個視圖寬度是相等的,或者視圖是在父視圖的頂部偏移必定的量,或者某一組視圖的寬度要平分父視圖等等功能。所以咱們分別爲子視圖定義了以下的擴展屬性:github
分別來定義視圖的左,上,右,下,水平中心,垂直中心,寬度,高度8個方位和尺寸的相對依賴對象,其中MyRelativePos用來定義相對的依賴位置類。它的定義以下:
@interface MyRelativePos :NSObject
//偏移
-(MyRelativePos* (^)(CGFloat val))offset;
//NSNumber, MyRelativePos對象,若是是centerXPos或者centerYPos則能夠傳NSArray,數組裏面裏面也必須是centerXPos,表示指定的視圖數組
//在父視圖中居中,好比: A.centerXPos.equalTo(@[B.centerXPos.offset(20)].offset(20)
//表示A和B在父視圖中居中往下偏移20,B在A的右邊,間隔20。
-(MyRelativePos* (^)(id val))equalTo;
@end
這個類中的offset用來指定某個位置的偏移值,而equalTo則用來指定依賴的某個位置和值,好比:
1.A視圖的左邊等於B視圖的右邊並偏移30個點: A.leftPos.equalTo(B.rightPos).offset(30)
2.A視圖的頂部和父視圖的頂部相等:A.topPos.equalTo(A.superView.topPos)
3.A視圖的中間在父視圖的中間: A.centerXPos.equalTo(A.superView.centerXPos); A.centerYPos.equalTo(A.superView.centerYPos)
4.A視圖的左邊偏移20: A.topPos.equalTo(@0).offset(20)
5.A,B,C三個視圖要總體在佈局視圖中水平居中:A.centerXPos.equalTo(@[B.centerXPos,C.centerXPos])
而對於相對尺寸則定義了MyRelativeDime類來定義寬度和高度,這個類的定義以下:
@interface MyRelativeDime :NSObject
//乘
-(MyRelativeDime* (^)(CGFloat val))multiply;
//加,用這個和equalTo的數組功能能夠實現均分子視圖寬度以及間隔的設定。
-(MyRelativeDime* (^)(CGFloat val))add;
//NSNumber, MyRelativeDime以及MyRelativeDime數組,數組的概念就是全部數組裏面的子視圖的尺寸平分父視圖的尺寸。
-(MyRelativeDime* (^)(id val))equalTo;
@end
尺寸定義中equalTo用來指定尺寸的值,能夠是NSNumber型指定絕對的尺寸,MyRelativeDime型指定相對的尺寸,NSArray[MyRelativeDime]來指定按比例分配父視圖的尺寸,mutiply用來指定在equalTo上指定的值的倍數比例,add指定在equalTo上指定的值的增量值(add方法更可能是用來指定間距)。
1.A視圖的寬度等於B視圖的寬度,A視圖的高度等於B視圖的高度的一半: A.widthDime.equalTo(B.widthDime); A.heightDime.equalTo(B.heightDime).multiply(0.5)
2.A視圖的高度等於B視圖的高度,並增長20: A.heightDime.equalTo(B.heightDime).add(20)
3.A,B,C三個視圖平分父視圖的寬度: A.widthDime.equalTo(@[B.widthDime, C.widthDime])
4.A視圖固定寬度爲20, B,C視圖按4:6平分剩餘的寬度: A.widthDime.equal(@20) B.widthDime.equalTo(@[A.widthDime, C.widthDime.multiply(0.6)]).multiply(0.4)
好了介紹了上述擴展的子視圖的擴展屬性後,咱們須要的只是創建一個MyRelativeLayout佈局視圖,而後設置好子視圖之間的相對依賴,而後添加進去就OK了。
1、相對佈局的子視圖的依賴。
上述的佈局就是用相對佈局實現,代碼很簡單,請參考以下:
- -(void)loadView
- {
-
- MyRelativeLayout *rl = [MyRelativeLayout new];
- rl.padding = UIEdgeInsetsMake(10, 10, 10, 10);
- rl.backgroundColor = [UIColor grayColor];
- self.view = rl;
-
- UILabel *lb1 = [UILabel new];
- [rl addSubview:lb1];
- lb1.text = @"你好";
- [lb1 sizeToFit];
- lb1.backgroundColor = [UIColor blueColor];
-
-
- lb1.leftPos.equalTo(rl.leftPos);
- lb1.topPos.equalTo(rl.topPos).offset(10);
- lb1.widthDime.equalTo(@60);
-
- UILabel *lb2 = [UILabel new];
- [rl addSubview:lb2];
- lb2.text = @"我好 hello";
- lb2.backgroundColor = [UIColor redColor];
-
- lb2.leftPos.equalTo(lb1.rightPos);
- lb2.topPos.equalTo(lb1.bottomPos);
- lb2.widthDime.equalTo(lb1.widthDime).add(30);
- lb2.heightDime.equalTo(lb1.heightDime).multiply(2).add(-10);
-
- UILabel *lb3 = [UILabel new];
- lb3.text = @"中間";
- lb3.backgroundColor = [UIColor greenColor];
- [rl addSubview:lb3];
-
- lb3.centerXPos.equalTo(rl.centerXPos);
- lb3.centerYPos.equalTo(rl.centerYPos);
- lb3.widthDime.equalTo(rl.widthDime).multiply(0.2);
- lb3.heightDime.equalTo(rl.heightDime).multiply(0.1);
-
- UILabel *lb4 = [UILabel new];
- lb4.text = @"他好";
- [lb4 sizeToFit];
- lb4.backgroundColor = [UIColor orangeColor];
- [rl addSubview:lb4];
-
-
- lb4.leftPos.equalTo(rl.leftPos);
- lb4.rightPos.equalTo(rl.rightPos);
- lb4.topPos.equalTo(@100);
-
- }
這段代碼中請注意lb4的設置,咱們會發現當咱們同時指定了左右和上下的依賴視圖時,寬度和高度就不須要指定出來,佈局會自動算出高度和寬度。注意代碼中咱們還爲相對佈局指定了padding的值,表示裏面的全部子視圖都會在padding以內。
2、相對佈局的子視圖的平均分配
有時候咱們的佈局的有些子視圖但願能按父視圖的尺寸來進行按某些規則進行分配,好比下面的佈局:
上面的視圖中,第一排的3個子視圖平分父視圖的寬度。第二排子視圖中第一個視圖寬度固定,剩餘的兩個平分。第三排的子視圖按0.2 0.3 0.5的比例來平分父視圖,代碼以下:
- -(void)loadView
- {
- MyRelativeLayout *rl = [MyRelativeLayout new];
- rl.padding = UIEdgeInsetsMake(0, 0, 0, 10);
- rl.backgroundColor = [UIColor grayColor];
- self.view = rl;
-
-
- UIView *v1 = [UIView new];
- v1.backgroundColor = [UIColor redColor];
- v1.heightDime.equalTo(@40);
- [rl addSubview:v1];
-
- UIView *v2 = [UIView new];
- v2.backgroundColor = [UIColor redColor];
- v2.heightDime.equalTo(@40);
- [rl addSubview:v2];
-
- UIView *v3 = [UIView new];
- v3.backgroundColor = [UIColor redColor];
- v3.heightDime.equalTo(@40);
- [rl addSubview:v3];
-
-
- v1.widthDime.equalTo(@[v2.widthDime.add(-10), v3.widthDime.add(-10)]).add(-10);
- v1.leftPos.offset(10);
- v2.leftPos.equalTo(v1.rightPos).offset(10);
- v3.leftPos.equalTo(v2.rightPos).offset(10);
-
-
-
- UIView *v4 = [UIView new];
- v4.backgroundColor = [UIColor greenColor];
- v4.topPos.equalTo(v1.bottomPos).offset(80);
- v4.heightDime.equalTo(@40);
- v4.widthDime.equalTo(@260);
- [rl addSubview:v4];
-
- UIView *v5 = [UIView new];
- v5.backgroundColor = [UIColor greenColor];
- v5.topPos.equalTo(v4.topPos);
- v5.heightDime.equalTo(@40);
- [rl addSubview:v5];
-
- UIView *v6 = [UIView new];
- v6.backgroundColor = [UIColor greenColor];
- v6.topPos.equalTo(v4.topPos);
- v6.heightDime.equalTo(@40);
- [rl addSubview:v6];
-
-
- v5.widthDime.equalTo(@[v4.widthDime.add(-10), v6.widthDime.add(-10)]).add(-10);
- v4.leftPos.offset(10);
- v5.leftPos.equalTo(v4.rightPos).offset(10);
- v6.leftPos.equalTo(v5.rightPos).offset(10);
-
-
-
-
- UIView *v7 = [UIView new];
- v7.backgroundColor = [UIColor blueColor];
- v7.topPos.equalTo(v4.bottomPos).offset(80);
- v7.heightDime.equalTo(@40);
- [rl addSubview:v7];
-
- UIView *v8 = [UIView new];
- v8.backgroundColor = [UIColor blueColor];
- v8.topPos.equalTo(v7.topPos);
- v8.heightDime.equalTo(@40);
- [rl addSubview:v8];
-
- UIView *v9 = [UIView new];
- v9.backgroundColor = [UIColor blueColor];
- v9.topPos.equalTo(v7.topPos);
- v9.heightDime.equalTo(@40);
- [rl addSubview:v9];
-
- v7.widthDime.equalTo(@[v8.widthDime.multiply(0.3).add(-10),v9.widthDime.multiply(0.5).add(-10)]).multiply(0.2).add(-10);
- v7.leftPos.offset(10);
- v8.leftPos.equalTo(v7.rightPos).offset(10);
- v9.leftPos.equalTo(v8.rightPos).offset(10);
-
-
-
- }
看代碼咱們發現,在分配視圖時指定了視圖之間的間距這須要藉助offset的調用來指定間距,由於是均分視圖咱們又須要爲視圖的寬度留有間隔,所以咱們須要藉助add的方法來將計算出的寬度減去間距的值,而同時咱們爲佈局視圖的padding的值,咱們設置了10的間距來控制最右邊的間距爲10。
有的時候咱們在均分子視圖時,當某個子視圖隱藏時其餘的剩餘的子視圖的寬度會進行調整,好比某個子視圖設置爲隱藏後,右邊的子視圖向左邊靠攏。而有的時候咱們但願當某個子視圖隱藏時,剩餘的部分從新填充慢佈局視圖的某個方位的尺寸,所以咱們能夠爲佈局視圖設置開關:
//均分寬度時當有隱藏子視圖,是否參與寬度計算,這個屬性只有在參與均分視圖的子視圖隱藏時纔有效,默認是NO
@property(nonatomic,assign)BOOL flexOtherViewWidthWhenSubviewHidden;
//均分高度時當有隱藏子視圖,是否參與高度計算,這個屬性只有在參與均分視圖的子視圖隱藏時纔有效,默認是NO
@property(nonatomic,assign)BOOL flexOtherViewHeightWhenSubviewHidden;
這兩個佈局視圖的屬性分別標明當某個子視圖數組均分父視圖時,而其中某個子視圖隱藏時,是否其餘視圖會從新分配寬度和高度。
3、相對佈局的高寬由子視圖決定
我在線性佈局的文章中有說明能夠經過wrapContent來決定是否佈局視圖的非方向是否由子視圖來決定。這時候咱們就不須要手動的指定佈局視圖的高度和寬度,而是由佈局視圖裏面的子視圖來決定佈局的尺寸,在android系統中咱們能夠設置wrapContent來設置佈局視圖的尺寸。一樣咱們在佈局中也分別提供了兩個屬性:
@property(nonatomic,assign)BOOL wrapContentWidth;
@property(nonatomic,assign)BOOL wrapContentHeight;
從上面的定義能夠看出,wrapContentWidth, wrapContentHeight則是指定佈局視圖的寬度和高度由子視圖決定,對於線性佈局來講若是是垂直方向的話wrapContentHeight是默認設置爲YES的,而水平方向則wrapContentWidth設置爲YES。而對於相對佈局來講二者默認都設置爲NO。
咱們先看結果界面:
- -(void)loadView
- {
- [super loadView];
-
- MyRelativeLayout *rl = [[MyRelativeLayout alloc] initWithFrame:CGRectMake(10, 10, 0, 0)];
- rl.padding = UIEdgeInsetsMake(10, 10, 10, 10);
- [self.view addSubview:rl];
- rl.wrapContentWidth = YES;
- rl.wrapContentHeight = YES;
- rl.backgroundColor = [UIColor grayColor];
-
- UILabel *lb1 = [UILabel new];
- lb1.leftPos.equalTo(rl.leftPos).offset(20);
- lb1.text = @"aaaa";
- lb1.backgroundColor = [UIColor redColor];
- [lb1 sizeToFit];
- lb1.rightPos.offset(20);
-
- [rl addSubview:lb1];
-
-
- UILabel *lb3 = [UILabel new];
- lb3.rightPos.equalTo(rl.rightPos).offset(5);
- lb3.topPos.equalTo(rl.topPos).offset(30);
- lb3.bottomPos.offset(10);
- lb3.text = @"ccc";
- lb3.backgroundColor = [UIColor redColor];
- [lb3 sizeToFit];
-
- [rl addSubview:lb3];
-
-
- UILabel *lb2 = [UILabel new];
- lb2.text = @"bbbb";
- lb2.backgroundColor = [UIColor blueColor];
-
- lb2.leftPos.equalTo(lb1.centerXPos);
- lb2.topPos.equalTo(lb1.bottomPos).offset(40);
- lb2.widthDime.equalTo(@50);
- lb2.heightDime.equalTo(@50);
- lb2.bottomPos.offset(40);
-
-
- [rl addSubview:lb2];
-
-
-
- }
上面的代碼中咱們能夠看到佈局視圖是沒有指定高度和寬度的,而是設置了屬性wrapContentWidth = YES, wrapContentHeight = YES
那麼佈局視圖的高度和寬度是怎麼計算出來的呢。咱們是經過計算出全部子視圖的位置和尺寸的最大高度和寬度來獲得佈局視圖的高度和寬度的,在上面的代碼中咱們看到lb3的右邊和佈局視圖的右邊相差5,可是佈局視圖這時候的寬度是沒有計算出來的,可是咱們仍是能夠這樣設置,由於lb1, lb2的尺寸和高度已經把佈局視圖撐開到足夠的高度和寬度了。
同時經過wrapContentXXX的佈局視圖的屬性咱們能夠動態的調整佈局視圖自己的高度和寬度,所以咱們也很適合將佈局視圖放入到一個UIScrollView中去。
4、一組視圖在佈局視圖中居中
有時候咱們但願佈局中的某些視圖總體居中,一個解決的方法是咱們爲這些視圖創建一個父視圖,而後讓這個父視圖居中,但這個前提是咱們須要新創建一個父視圖來包圍這批視圖。採用相對佈局的方法是不須要再新建一個附加的父視圖的。咱們先看界面。
界面中咱們看到上面的3個視圖總體是在父視圖的水平中間的,而下面3個則是在父視圖的垂直中間的,這個功能的代碼實現很簡單:
- -(void)loadView
- {
-
- MyRelativeLayout *rl = [MyRelativeLayout new];
- rl.backgroundColor = [UIColor grayColor];
- self.view = rl;
-
-
- UILabel *lb1 = [UILabel new];
- lb1.text = @"abcdefg";
- [lb1 sizeToFit];
- lb1.backgroundColor = [UIColor redColor];
- lb1.topPos.offset(100);
- [rl addSubview:lb1];
-
- UILabel *lb2 = [UILabel new];
- lb2.text = @"abcdefgfd";
- [lb2 sizeToFit];
- lb2.backgroundColor = [UIColor blueColor];
- lb2.topPos.offset(100);
- [rl addSubview:lb2];
-
-
- UILabel *lb3 = [UILabel new];
- lb3.text = @"abc";
- [lb3 sizeToFit];
- lb3.backgroundColor = [UIColor greenColor];
- lb3.topPos.offset(100);
- [rl addSubview:lb3];
-
-
-
-
- lb1.centerXPos.equalTo(@[lb2.centerXPos.offset(5), lb3.centerXPos.offset(10)]);
-
-
- UILabel *lb4 = [UILabel new];
- lb4.text = @"你好";
- [lb4 sizeToFit];
- lb4.backgroundColor = [UIColor orangeColor];
- [rl addSubview:lb4];
- lb4.leftPos.equalTo(lb1.leftPos);
- lb4.topPos.equalTo(lb2.bottomPos).offset(10);
-
-
-
- UILabel *lb5 = [UILabel new];
- lb5.text = @"abcdefg";
- [lb5 sizeToFit];
- lb5.backgroundColor = [UIColor redColor];
- lb5.centerXPos.equalTo(rl.centerXPos);
- [rl addSubview:lb5];
-
- UILabel *lb6 = [UILabel new];
- lb6.text = @"abcdefgfd";
- [lb6 sizeToFit];
- lb6.backgroundColor = [UIColor blueColor];
- lb6.centerXPos.equalTo(rl.centerXPos);
- [rl addSubview:lb6];
-
-
- UILabel *lb7 = [UILabel new];
- lb7.text = @"abc";
- [lb7 sizeToFit];
- lb7.backgroundColor = [UIColor greenColor];
- lb7.centerXPos.equalTo(rl.centerXPos);
- [rl addSubview:lb7];
-
- lb5.centerYPos.equalTo(@[lb6.centerYPos.offset(5), lb7.centerYPos.offset(10)]);
-
-
- }
代碼中咱們能夠看到lb1,lb2,lb3是水平居中的,而lb5,lb6,lb7則是垂直居中的。咱們只須要分別爲lb1.centerXPos,lb5.centerYPos指定一個關聯的居中的數組就能夠了,數組的內容就是其餘關聯的視圖的對應的centerXPos或者centerYPos。同時咱們能夠指定offset來代表視圖之間的偏移的距離。
代碼以下:
- -(void)loadView
- {
- MyRelativeLayout *rl = [MyRelativeLayout new];
- self.view = rl;
-
- UILabel *lb1up = [UILabel new];
- lb1up.text = @"左上面";
- lb1up.backgroundColor = [UIColor greenColor];
- lb1up.font = [UIFont systemFontOfSize:17];
- [lb1up sizeToFit];
- [rl addSubview:lb1up];
-
- UILabel *lb1down = [UILabel new];
- lb1down.text = @"我左在下面";
- lb1down.backgroundColor = [UIColor greenColor];
- [lb1down sizeToFit];
- [rl addSubview:lb1down];
-
-
- UILabel *lb2up = [UILabel new];
- lb2up.text = @"我在中間上面";
- lb2up.backgroundColor = [UIColor greenColor];
- lb2up.font = [UIFont systemFontOfSize:12];
- [lb2up sizeToFit];
- [rl addSubview:lb2up];
-
- UILabel *lb2down = [UILabel new];
- lb2down.text = @"中";
- lb2down.backgroundColor = [UIColor greenColor];
- [lb2down sizeToFit];
- [rl addSubview:lb2down];
-
-
- UILabel *lb3up = [UILabel new];
- lb3up.text = @"右上";
- lb3up.backgroundColor = [UIColor greenColor];
- [lb3up sizeToFit];
- [rl addSubview:lb3up];
-
- UILabel *lb3down = [UILabel new];
- lb3down.text = @"右邊的下方";
- lb3down.backgroundColor = [UIColor greenColor];
- lb3down.font = [UIFont systemFontOfSize:16];
- [lb3down sizeToFit];
- [rl addSubview:lb3down];
-
-
- lb1up.centerYPos.equalTo(@[lb1down.centerYPos.offset(10)]);
- lb2up.centerYPos.equalTo(@[lb2down.centerYPos.offset(10)]);
- lb3up.centerYPos.equalTo(@[lb3down.centerYPos.offset(10)]);
-
-
- lb1up.centerXPos.equalTo(@[lb2up.centerXPos.offset(60),lb3up.centerXPos.offset(60)]);
-
-
- lb1down.centerXPos.equalTo(lb1up.centerXPos);
- lb2down.centerXPos.equalTo(lb2up.centerXPos);
- lb3down.centerXPos.equalTo(lb3up.centerXPos);
-
-
-
-
- }
經過代碼咱們能夠看出來雖然是有上下兩排視圖,可是咱們能夠經過centerYPos和centerXPos的值設置數組的方式來實現一組視圖的居中顯示。
5、總結
好了,相對視圖的介紹就佈局到這裏了,到這裏我分別爲你介紹了框架佈局,線性佈局和相對佈局方面的東西,經過這三個佈局的使用咱們徹底能夠擺脫對IOS的自動佈局和sizeClass的使用,而用更加簡單清晰的方法來佈局您的界面,但願個人庫能對您提供很是有利的幫助,若是您須要用個人庫來編碼,那麼就請到: