關於界面佈局,apple的策略已經趨於成熟,autolayout的優點在開發中也已經展示的淋漓盡致。除了使用storyBoard進行佈局約束的拖拽,有時咱們也須要在代碼中進行autolayout的佈局設置,Masonry庫能夠方便的建立約束屬性,實際上,咱們也沒有必要再使用系統原生的代碼來建立和設置約束,這篇博客只做爲使用的方法備忘。前幾篇佈局介紹的連接以下:app
使用autoresizing進行界面佈局:http://my.oschina.net/u/2340880/blog/423357框架
初識autolayout佈局模型:http://my.oschina.net/u/2340880/blog/423500工具
用代碼來作視圖間的相關約束,那麼就必定要將「約束」也進行對象化,在iOS6以後,引入了autolayout這個概念,相應的也增長了NSLayoutConstraint這個對象,這個對象就是專門用來進行約束佈局的設置對象。經過這個對象,咱們能夠設置相似視圖對象之間的間距,約束的寬高,比例等屬性。建立NSLayoutConstraint對象的方法有兩種,下面咱們分別介紹:佈局
所謂Objective-C風格的方法,就是經過原生枚舉和一些屬性設置來建立NSLayoutConstraint對象。使用NSLayoutConstraint類的以下方法:動畫
+(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;
先來介紹下方法中的參數都是什麼意義,咱們應該怎麼用他們:spa
view1:要添加約束的視圖對象。.net
attr1:要約束的對象屬性,這個就是一些枚舉,以下:設計
typedef NS_ENUM(NSInteger, NSLayoutAttribute) { NSLayoutAttributeLeft = 1,//左 NSLayoutAttributeRight,//右 NSLayoutAttributeTop,//上 NSLayoutAttributeBottom,//下 NSLayoutAttributeLeading,//起始邊,相似左,只在某些從右向左排列的語言中和NSLayoutAttributeLeft有大區別 NSLayoutAttributeTrailing,//結束邊 NSLayoutAttributeWidth,//寬度 NSLayoutAttributeHeight,//高度 NSLayoutAttributeCenterX,//x中心 NSLayoutAttributeCenterY,//y中心 NSLayoutAttributeBaseline,//基線 NSLayoutAttributeLastBaseline = NSLayoutAttributeBaseline, NSLayoutAttributeFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0), //下面的屬性是設置的邊距 意義和上面相似 對應左,右等邊距 NSLayoutAttributeLeftMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeRightMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeTopMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeBottomMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeLeadingMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeTrailingMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeCenterXWithinMargins NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeCenterYWithinMargins NS_ENUM_AVAILABLE_IOS(8_0), //無,後面會說應用場景 NSLayoutAttributeNotAnAttribute = 0 };
relation:約束的選項,對應<=,==,>=這些,枚舉以下:code
typedef NS_ENUM(NSInteger, NSLayoutRelation) { NSLayoutRelationLessThanOrEqual = -1,//<= NSLayoutRelationEqual = 0,//== NSLayoutRelationGreaterThanOrEqual = 1,//>= };
view2:與之對應添加約束的視圖對象,例如,如過我要設置view1的上邊距離父視圖的上邊必定間距,這個view2就是view1的父視圖,若是我要設置view1與另外一個視圖必定距離,這個view2就是另外一個視圖。orm
attr2:view2的要約束的屬性,和attr1含義同樣。
multiplie:約束的比例,好比view1的寬是view2的寬的兩倍,這個multiplie就是2.
C:這是具體的約束值
對於這些屬性,文檔上有這樣的解釋:view1.attr1 = view2.attr2 * multiplier + constant
例如,咱們建立一個label,將它的寬高固定爲100*100,位置放在屏幕的中央,咱們可使用以下的約束代碼:
UILabel * label = [[UILabel alloc]init]; label.numberOfLines = 0; //使用代碼佈局 須要將這個屬性設置爲NO label.translatesAutoresizingMaskIntoConstraints = NO; label.backgroundColor = [UIColor redColor]; //建立x居中的約束 NSLayoutConstraint * constraintx = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]; //建立y居中的約束 NSLayoutConstraint * constrainty = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]; //建立寬度約束 NSLayoutConstraint * constraintw = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:100]; //建立高度約束 NSLayoutConstraint * constrainth = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:100]; //添加約束以前,必須將視圖加在父視圖上 [self.view addSubview:label]; [self.view addConstraints:@[constraintx,constrainty,constrainth,constraintw]];
效果以下:
能夠發現,一個如此簡單的約束方式,咱們用這樣的代碼要寫這麼一大坨,麻煩並且不直觀。因而,apple又提供給咱們下面一種方式。
看到這個小標題是否是眼前一亮,這個標題不是我憑空想象出來的,apple的文檔上就是這麼寫的。十分可愛,對吧。相對於NSLayoutConstraint中的建立方法以下:
+ (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(nullable NSDictionary<NSString *,id> *)metrics views:(NSDictionary<NSString *, id> *)views;
那麼咱們先來看,什麼是格式化的字符串約束。
說到格式化的字符串約束,要先提一個東西VFL:visual format language——格式化約束語言,這又是什麼鬼?確實,這個東西寫出來第一眼看上去真的不知道是什麼鬼,就好比要設置一個label,距離上邊100,左邊20,寬高都爲100,代碼以下:
label.numberOfLines = 0; label.translatesAutoresizingMaskIntoConstraints = NO; label.backgroundColor = [UIColor redColor]; //label.text=@"12332322132131233213213"; [self.view addSubview:label]; NSArray * constraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-20-[label(100@1000)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(label)]; NSArray * constraintArray2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-100-[label(100)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(label)]; [self.view addConstraints:constraintArray]; [self.view addConstraints:constraintArray2];
效果以下:
代碼少了不少,對吧,可是中間那部分字符串什麼玩意?下面咱們來解釋一下。
VFL語言我我的而言,他很相似於古代的象形文字(不知道是否apple的工程師從其中獲得靈感),對佈局的約束設置是直觀的用符號表達出來的,例如:
H:|-20-[label(100@1000)]
前面的H表明是水平的佈局仍是垂直的佈局,H表明水平,V表示垂直,|表示父視圖的邊沿,-20-表示距離20px,[]內是要佈局擺放的視圖對象名,()中是約束的尺寸,H下則爲寬度,V下則爲高度,@後面的數字表明優先級。
建立方法中的options參數,用來設置對齊模式,不須要能夠寫0:
typedef NS_OPTIONS(NSUInteger, NSLayoutFormatOptions) { NSLayoutFormatAlignAllLeft = (1 << NSLayoutAttributeLeft), NSLayoutFormatAlignAllRight = (1 << NSLayoutAttributeRight), NSLayoutFormatAlignAllTop = (1 << NSLayoutAttributeTop), NSLayoutFormatAlignAllBottom = (1 << NSLayoutAttributeBottom), NSLayoutFormatAlignAllLeading = (1 << NSLayoutAttributeLeading), NSLayoutFormatAlignAllTrailing = (1 << NSLayoutAttributeTrailing), NSLayoutFormatAlignAllCenterX = (1 << NSLayoutAttributeCenterX), NSLayoutFormatAlignAllCenterY = (1 << NSLayoutAttributeCenterY), NSLayoutFormatAlignAllBaseline = (1 << NSLayoutAttributeBaseline), NSLayoutFormatAlignAllLastBaseline = NSLayoutFormatAlignAllBaseline, NSLayoutFormatAlignAllFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0) = (1 << NSLayoutAttributeFirstBaseline), NSLayoutFormatAlignmentMask = 0xFFFF, /* choose only one of these three */ NSLayoutFormatDirectionLeadingToTrailing = 0 << 16, // default NSLayoutFormatDirectionLeftToRight = 1 << 16, NSLayoutFormatDirectionRightToLeft = 2 << 16, NSLayoutFormatDirectionMask = 0x3 << 16, };
metrics參數是屬性替換字典,例如咱們上邊用到的距離左邊界20,若是這個20是一個變量width,咱們能夠將20的地方換成width,而後配置這個字典:@{@"width":@20},這樣,在佈局時,系統會把width換成20。
views是對象的映射字典,原理也是將字符串中的對象名label映射成真實的對象,NSDictionaryOfVariableBindings會幫咱們生成這樣的字典,咱們只須要想對象傳進去便可,若是要手動建立這字典,格式以下:@{@"label":label}.
仔細觀察QQ或者其餘聊天工具的app上的輸入框,會發現他很是智能,寬度會隨着咱們輸入文字的行數進行自適應,而且這個寬度不是無限增大的,當咱們文字多到必定行數,寬度會保持不變,文本框能夠進行內容滑動,若是不用autolayout,這個功能會比較棘手一些,可是使用它,會發現這是如此的容易:
@interface ViewController ()<UITextViewDelegate> { UITextView * textView ; NSArray * array1; NSArray * array2; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. textView = [[UITextView alloc]init]; textView.layer.borderColor = [[UIColor grayColor]CGColor]; textView.layer.borderWidth = 1; textView.translatesAutoresizingMaskIntoConstraints = NO; textView.delegate=self; [self.view addSubview:textView]; array1 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-100-[textView]-100-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(textView)]; array2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-150-[textView(30)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(textView)]; [self.view addConstraints:array1]; [self.view addConstraints:array2]; } -(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{ //當文本高度大於textView的高度而且小於100時,更新約束 if (textView.contentSize.height>textView.frame.size.height&&textView.contentSize.height<100) { float hight =textView.contentSize.height; //將之前的移除掉 [self.view removeConstraints:array1]; [self.view removeConstraints:array2]; array1 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-100-[textView]-100-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(textView)]; array2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-150-[textView(hight)]" options:0 metrics:@{@"hight":[NSNumber numberWithFloat:hight]} views:NSDictionaryOfVariableBindings(textView)]; [self.view addConstraints:array1]; [self.view addConstraints:array2]; } //更新約束 [self.view updateConstraintsIfNeeded]; return YES; }
如今,當咱們進行輸入的時候,textView的高度能夠自適應文字行數了。
這一點很是coll,上面咱們已經實現了textView隨文本的行數高度進行自適應,可是變化的效果十分生硬,還要apple的動畫框架支持autolayout,把剛纔調用更新約束的地方進行以下修改:
[UIView animateWithDuration:1 animations:^{ [self.view layoutIfNeeded]; }];
試試看,變換的效果已經很是平滑了。
專一技術,熱愛生活,交流技術,也作朋友。
——琿少 QQ羣:203317592