一、概要
通過對iOS8介面佈局的學習和總結,發現autolayout才是主角,autolayout是iOS6引入的新特性,當時還粗淺的學習了下,可是沒有真正應用到專案中。隨著iOS裝置尺寸逐漸碎片化,純粹的hard
code方式UI佈局將會走向死角,而autoresizing方式也有其侷限性,所以無論如何autolayout都將成為UI佈局的重要方式。
前兩篇以發燒友心態對iOS8介面佈局的主要元素size class和autolayout進行了探索,發現要完全掌握autolayout需要大量的時間去實踐總結。所以深入思考autolayout是很有必要的。你可能有和我同樣的疑問,如下:
1、以後一律使用autolayout嗎?除了在storyboard中使用autolayout,程式碼方式autolayout如何使用?
2、好像忽略了一個重要問題,就是view動畫在autolayout如何實現?
3、autolayout有沒有侷限性和解決不了的問題?相容性怎麼樣?效率怎麼樣?
4、……
二、研究開始
1、直接說以後都應該使用storyboard+autolayout感覺是不負責的說法,讀了好多網路的帖子,最後總結如下情況使用autolayout會有幫助:
a 當需要展示的內容很多並且尺寸不固定;
b 程式需支援螢幕旋轉(主要是iPad程式,iPhone程式橫屏的場景有點非主流);
c 程式通用於iPhone和iPad;
但storyboard中使用autolayout有利有弊,好處當然是視覺化,實現簡單功能很節省時間,但也有弊端,例如不小心移動一個控制元件就會讓弄亂那些約束。拋開storyboard而使用autolayout,就需要程式碼定義約束了,而且程式碼量也不是很大。當app中一些view的出現時根據網路資料來決定的時候,程式碼方式可能更合適。
先看一個簡單的Demo:
例子1:新建一個Single View Application template專案Demo4,在rootView上新增一個綠顏色的view,使新新增的view四個邊距離superView四邊20點寬
效果如圖:
使用storyboard來實現這個效果很簡單,選中綠色view,然後新增4個相對於superview的邊界約束,約束的數值設定為20,然後Update Frame就可以了,因為不區分iOS裝置,所以size class可以設定為預設的wAny hAny。Demo下載
接下來使用程式碼來實現UI佈局,目前有3種方法可以使用:(1)最基本的約束實現方式;(2)特殊格式化語言的約束實現方式;(3)第三方UIView-AutoLayout
(1)最基本的約束實現方式
-
- (void)viewDidLoad {
-
[super viewDidLoad];
-
-
self.view.translatesAutoresizingMaskIntoConstraints =NO;
-
-
UIView *newView = [UIView new];
-
newView.backgroundColor = [UIColor greenColor];
-
[self.view addSubview:newView];
-
-
newView.translatesAutoresizingMaskIntoConstraints =NO;
-
-
NSLayoutConstraint *constraint = nil;
-
-
constraint = [NSLayoutConstraint constraintWithItem:newView
-
attribute:NSLayoutAttributeLeading
-
relatedBy:NSLayoutRelationEqual
-
toItem:self.view
-
attribute:NSLayoutAttributeLeading
-
multiplier:1.0f
-
constant:20];
-
[self.view addConstraint:constraint];
-
-
constraint = [NSLayoutConstraint constraintWithItem:newView
-
attribute:NSLayoutAttributeTrailing
-
relatedBy:NSLayoutRelationEqual
-
toItem:self.view
-
attribute:NSLayoutAttributeTrailing
-
multiplier:1.0f
-
constant:-20];
-
[self.view addConstraint:constraint];
-
-
constraint = [NSLayoutConstraint constraintWithItem:newView
-
attribute:NSLayoutAttributeTop
-
relatedBy:NSLayoutRelationEqual
-
toItem:self.view
-
attribute:NSLayoutAttributeTop
-
multiplier:1.0f
-
constant:20];
-
[self.view addConstraint:constraint];
-
-
constraint = [NSLayoutConstraint constraintWithItem:newView
-
attribute:NSLayoutAttributeBottom
-
relatedBy:NSLayoutRelationEqual
-
toItem:self.view
-
attribute:NSLayoutAttributeBottom
-
multiplier:1.0f
-
constant:-20];
-
[self.view addConstraint:constraint];
-
}
(2)特殊格式化語言的約束實現方式
-
- (void)viewDidLoad {
-
[super viewDidLoad];
-
self.view.translatesAutoresizingMaskIntoConstraints =NO;
-
-
UIView *newView = [UIView new];
-
newView.backgroundColor = [UIColor greenColor];
-
[self.view addSubview:newView];
-
newView.translatesAutoresizingMaskIntoConstraints =NO;
-
-
NSMutableArray *constraintArray = [NSMutableArray array];
-
-
[constraintArray addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-20-[newView]-20-|"
-
options:0
-
metrics:nil
-
views:NSDictionaryOfVariableBindings(newView, self.view)]];
-
[constraintArray addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-20-[newView]-20-|"
-
options:0
-
metrics:nil
-
views:NSDictionaryOfVariableBindings(newView, self.view)]];
-
[self.view addConstraints:constraintArray];
-
}
(3)第三方UIView-AutoLayout
-
<span style="color:#000000;">- (void)viewDidLoad {
-
[super viewDidLoad];
-
self.view.translatesAutoresizingMaskIntoConstraints =NO;
-
-
UIView *newView = [UIView new];
-
newView.backgroundColor = [UIColor greenColor];
-
[self.view addSubview:newView];
-
newView.translatesAutoresizingMaskIntoConstraints =NO;
-
-
[newView autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:20.0f];
-
[newView autoPinEdgeToSuperviewEdge:ALEdgeTrailing withInset:20.0f];
-
[newView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:20.0f];
-
[newView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:20.0f];
-
}</span><span style="color:#ff9900;">
-
</span>
以上3種方式都實現了我們想要的效果,看來程式碼實現autolayout也不是那麼複雜!
例子2:通過上邊例子我們實現一個簡單的UI佈局,下面來一個稍微複雜點的,把上一篇中提到3個view佈局的那個例子用程式碼佈局實現一下,但難度有所增加,當size class切換的時候,頁面佈局發生相應的改變,效果如圖:
首先初始化3個View:
-
- (UIView *)alView
-
{
-
UIView *newView = [UIView new];
-
newView.translatesAutoresizingMaskIntoConstraints =NO;
-
-
return newView;
-
}
-
UIView *greenView = [self alView];
-
greenView.backgroundColor = [UIColor greenColor];
-
[self.view addSubview:greenView];
-
UIView *yellowView = [self alView];
-
yellowView.backgroundColor = [UIColor yellowColor];
-
[self.view addSubview:yellowView];
-
UIView *blueView = [self alView];
-
blueView.backgroundColor = [UIColor blueColor];
-
[self.view addSubview:blueView];
接下來適配豎屏的約束:
-
- (NSMutableArray *) portraitConstraints:(UIView *)greenView :(UIView *)yellowView :(UIView *)blueView
-
{
-
NSMutableArray *constraintArray = [NSMutableArray array];
-
-
[constraintArray addObjectsFromArray:[NSLayoutConstraint
-
constraintsWithVisualFormat:@"H:|-20-[greenView]-20-[yellowView(==greenView)]-20-|" options:0 metrics:nil
-
views:NSDictionaryOfVariableBindings(greenView, yellowView)]];
-
[constraintArray addObjectsFromArray:[NSLayoutConstraint
-
constraintsWithVisualFormat:@"V:|-20-[greenView]-20-[blueView(==greenView)]-20-|" options:0 metrics:nil
-
views:NSDictionaryOfVariableBindings(greenView, blueView)]];
-
-
[constraintArray addObjectsFromArray:[NSLayoutConstraint
-
constraintsWithVisualFormat:@"V:|-20-[yellowView]-20-[blueView(==yellowView)]-20-|" options:0 metrics:nil
-
views:NSDictionaryOfVariableBindings(yellowView, blueView)]];
-
-
[constraintArray addObjectsFromArray:[NSLayoutConstraint
-
constraintsWithVisualFormat:@"H:|-20-[blueView]-20-|" options:0 metrics:nil
-
views:NSDictionaryOfVariableBindings(blueView)]];
-
-
return constraintArray;
-
}
然後橫屏的約束:
-
- (NSMutableArray *) landscapeConstraints:(UIView *)greenView :(UIView *)yellowView :(UIView *)blueView
-
{
-
NSMutableArray *constraintArray = [NSMutableArray array];
-
-
[constraintArray addObjectsFromArray:[NSLayoutConstraint
-
constraintsWithVisualFormat:@"H:|-20-[greenView]-20-[yellowView(==greenView)]-20-|" options:0 metrics:nil
-
views:NSDictionaryOfVariableBindings(greenView, yellowView)]];
-
-
[constraintArray addObjectsFromArray:[NSLayoutConstraint
-
constraintsWithVisualFormat:@"V:|-20-[blueView]-20-[greenView(==blueView)]-20-|" options:0 metrics:nil
-
views:NSDictionaryOfVariableBindings(greenView, blueView)]];
-
-
[constraintArray addObjectsFromArray:[NSLayoutConstraint
-
constraintsWithVisualFormat:@"V:|-20-[blueView]-20-[yellowView(==blueView)]-20-|" options:0 metrics:nil
-
views:NSDictionaryOfVariableBindings(yellowView, blueView)]];
-
-
[constraintArray addObjectsFromArray:[NSLayoutConstraint
-
constraintsWithVisualFormat:@"H:|-20-[blueView]-20-|" options:0 metrics:nil
-
views:NSDictionaryOfVariableBindings(blueView)]];
-
-
return constraintArray;
-
}
最後還要處理螢幕旋轉:
-
- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection
-
withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator
-
{
-
[super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator];
-
-
[coordinator animateAlongsideTransition:^(id <UIViewControllerTransitionCoordinatorContext> context) {
-
if (newCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact) {
-
NSLog(@"%s----%d", __FUNCTION__, __LINE__);
-
[self.view removeConstraints:self.view.constraints];
-
[self.view addConstraints:[self landscapeConstraints:self.greenView_ :self.yellowView_ :self.blueView_]];
-
} else {
-
NSLog(@"%s----%d", __FUNCTION__, __LINE__);
-
[self.view removeConstraints:self.view.constraints];
-
[self.view addConstraints:[self portraitConstraints:self.greenView_ :self.yellowView_ :self.blueView_]];
-
}
-
[self.view setNeedsLayout];
-
} completion:nil];
-
}
這樣就實現了我們預期的效果,總結下來,auotlayout就是給view新增足夠的約束,讓view系統可以根據約束來計算出一個view的frame。動手練習一下吧!
2、view動畫在autolayout實現
當佈局發生改變時,相當於對子view進行重新佈局,而子view重新佈局呼叫 layoutIfNeeded,所以動畫可以這樣實現:
-
- (void)animateConstraints
-
{
-
[UIView animateWithDuration:0.5 animations:^{
-
[self.view layoutIfNeeded];
-
}];
-
}
Github上已經有Demo了!
3、autolayout有沒有侷限性和解決不了的問題?相容性怎麼樣?效率怎麼樣?
autolayout對view transforms支援的不好,這裡有帖子詳細描述了這個問題。
至於相容性,只從iOS6就已經提出了autolayout的概念,現在iOS5系統不是很多了,甚至iOS6系統都已經升級為iOS7,未來一段時間大部分使用者應該是使用iOS7和iOS8系統,所以相容性問題不會太大,但size
class是iOS8才有的概念,所以還有有一定的適配工作量。
效率話題這裡有提到,有時間再細研究。
結束語:時間和體力總是有限的,標題是autolayout詳解,可想達到詳解還需要更多的時間去實踐和總結,還有一些細節沒有體現出來:
例如:
-
[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-20-[newView]-20-|"options:0 metrics:nil views:NSDictionaryOfVariableBindings(newView, self.view)]