關于自動布局的那點事兒NSLayoutConstraint VFL Masonry

  1. NSLayoutConstraint
  2. VFL
  3. Masonry

在開發(fā)中我們經(jīng)常會將XIB囤耳、StoryBoard配合AutoLayout 來完成界面的適配,但是很多時候如果我們需要封裝一個庫卻不想在庫中使用XIB或者StoryBoard,此時我們就需要通過純代碼的方式來對屏幕進行適配了,這也是今天我想講的主要內(nèi)容,即 如何用純代碼的方式來進行屏幕適配

NSLayoutConstraint

我們先說一下使用 NSLayoutConstraint的具體步驟:

  • 利用 NSLayoutConstraint 類創(chuàng)建具體的約束對象,注意一個 NSLayoutConstraint實例代表一條約束寓娩。
  • 添加約束對象到相應的 view 上,蘋果為我們提供了兩個方法:
- (void)addConstraint:(NSLayoutConstraint *)constraint NS_AVAILABLE_IOS(6_0);
- (void)addConstraints:(NSArray<__kindof NSLayoutConstraint *> *)constraints NS_AVAILABLE_IOS(6_0);

下面我們來看一個具體的例子:
NSLayoutConstraint實現(xiàn)一個寬度高度為100,在父控件中垂直水平居中的效果

     UIView *redView = [[UIView alloc] init];
   redView.backgroundColor = [UIColor redColor];
   [self.view addSubview:redView];
   
   //要先禁止 autoresizing 功能
   redView.translatesAutoresizingMaskIntoConstraints = NO;
   
   /**
    *參數(shù)一:要約束的控件
    *參數(shù)二:約束的類型(做怎樣的約束)
    *參數(shù)三:與參照控件之間的關系
    *參數(shù)四:參照的控件
    *參數(shù)五:約束的類型(做怎樣的約束)
    *參數(shù)六:乘數(shù)
    *參數(shù)七:常量
    */
    // 添加寬度約束:父控件的一半
   NSLayoutConstraint *consW = [NSLayoutConstraint constraintWithItem:redView
                                                   attribute:NSLayoutAttributeWidth
                                                   relatedBy:NSLayoutRelationEqual
                                                   toItem:self.view
                                                   attribute:NSLayoutAttributeWidth
                                                   multiplier:0.5
                                                   constant:0.0
                                ];
   // 添加高度約束:父控件的一半
   NSLayoutConstraint *consH = [NSLayoutConstraint constraintWithItem:redView
                                                   attribute:NSLayoutAttributeHeight
                                                   relatedBy:NSLayoutRelationEqual
                                                   toItem:self.view attribute:NSLayoutAttributeHeight
                                                   multiplier:0.5
                                                   constant:0.0
                                ];
   // 水平居中
   NSLayoutConstraint *consX = [NSLayoutConstraint constraintWithItem:redView
                                                   attribute:NSLayoutAttributeCenterX
                                                   relatedBy:NSLayoutRelationEqual
                                                   toItem:self.view
                                                   attribute:NSLayoutAttributeCenterX
                                                   multiplier:1.0
                                                   constant:0.0
                                      ];
   // 垂直居中
   NSLayoutConstraint *consY = [NSLayoutConstraint constraintWithItem:redView
                                                   attribute:NSLayoutAttributeCenterY
                                                   relatedBy:NSLayoutRelationEqual
                                                   toItem:self.view
                                                   attribute:NSLayoutAttributeCenterY
                                                   multiplier:1.0
                                                   constant:0.0
                                        ];
   
   [self.view addConstraints:@[consW,consH,consX,consY]];

效果圖如下:

image

注意事項

  1. 要先禁止 autoresizing 功能 redView.translatesAutoresizingMaskIntoConstraints = NO;
  2. 添加約束之前辫诅,一定要保證相關控件都已經(jīng)添加到對應的父控件上了
  3. 因為iOS中原點在左上角所以使用offset時注意right和bottom用負數(shù)

添加約束的規(guī)則
在創(chuàng)建約束之后催式,需要將其添加到作用的view上,在添加時要注意目標view需要遵循以下規(guī)則:

  1. 對于兩個同層級view之間的約束關系临庇,添加到它們的父view上
    image
  2. 對于兩個不同層級view之間的約束關系,添加到他們最近的共同父view上
    image
  3. 對于有層次關系的兩個view之間的約束關系麸塞,添加到層次較高的父view上
    image

VFL(Visual Format Language)

通過上面寬高為100秃臣,在父控件中垂直水平居中的簡單效果可以看到復雜程度,蘋果公司為了簡化這種操作,設計了一種 可視化格式語言即:VFL

VFL的思想其實很簡單,它將約束分成了兩塊即水平方向(H:)和垂直方向(V:)

其實蘋果官方文檔對 這一塊 描述的特別詳細,這里我就直接貼圖了:


寬度約束
image

image

image

我們來看看官方為我們提供的API

/**
 *  參數(shù)一:format     VFL格式構成的字符串,用以表達想要添加的約束
 *  參數(shù)二:options    對齊方式哪工,是一個枚舉值
 *  參數(shù)三:metrics    一般傳入以間距為KEY的字典奥此,如: @{ @"margin":@20},KEY要與format參數(shù)里所填寫的“margin”相同
 *  參數(shù)三:views      傳入約束中提到的View雁比,也是要傳入字典稚虎,但是KEY一定要和format參數(shù)里所填寫的View名字相同
 *
 *  @return 返回約束的數(shù)組
 */
+ (NSArray *)constraintsWithVisualFormat:(NSString *)format
                                 options:(NSLayoutFormatOptions)options
                                 metrics:(NSDictionary *)metrics
                                 views:(NSDictionary *)views;

注意:
因為參數(shù)三和參數(shù)四需要傳入字典,字典的KEY要和Format中的名字相同,如果我們自己去寫 第一比較麻煩,第二不小心還容易寫錯,所以蘋果為我們提供了一個強大的: NSDictionaryOfVariableBindings

/* This macro is a helper for making view dictionaries 
for +constraintsWithVisualFormat:options:metrics:views: 
 NSDictionaryOfVariableBindings(v1, v2, v3) is equivalent to 
 [NSDictionary dictionaryWithObjectsAndKeys:v1, @"v1", v2, @"v2", v3, @"v3", nil];
 */
#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)

為了更清晰的展示VFL的用法 下面我用一個例子來展示一下:
??例子:用VFL實現(xiàn)下面的效果:View寬高相等,邊距20

image

      UIView *blueView = [[UIView alloc] init];
    blueView.backgroundColor = [UIColor blueColor];
    // 不要將AutoresizingMask轉為Autolayout的約束
    blueView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:blueView];
    
    UIView *redView = [[UIView alloc] init];
    redView.backgroundColor = [UIColor redColor];
    // 不要將AutoresizingMask轉為Autolayout的約束
    redView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:redView];
    
    // 間距
    NSNumber *margin = @20;
    
    // 添加水平方向的約束
    NSString *hFormat = @"H:|-margin-[blueView]-margin-[redView(==blueView)]-margin-|";
    
    //把要添加約束的View轉成字典
    NSDictionary *views = NSDictionaryOfVariableBindings(blueView, redView);
    NSDictionary *mertrics = NSDictionaryOfVariableBindings(margin);
    //options: 這里設置底部和頂部對齊
    NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:hFormat
                                               options:NSLayoutFormatAlignAllTop
                                                     | NSLayoutFormatAlignAllBottom
                                                metrics:mertrics
                                                views:views];
    [self.view addConstraints:constraints];
    
    // 添加豎直方向的間距
    NSNumber *height = @40;
    NSString *vFormat = @"V:[blueView(height)]-margin-|";
    NSDictionary *mertrics2 = NSDictionaryOfVariableBindings(margin, height);
    NSArray *constraints2 = [NSLayoutConstraint constraintsWithVisualFormat:vFormat
                                                options:kNilOptions
                                                metrics:mertrics2
                                                views:views];
    [self.view addConstraints:constraints2];

VFL也是有缺陷的,這話是蘋果自己說的 有圖有真相

image

所以 這也就引出了下面我想講的東西 Masonry

Masonry

其實關于Masonry的用法 其github上的主頁已經(jīng)描述的非常清楚直白了,這里我們直接用Masonry來實現(xiàn)最開始的小例子

    UIView *redView = [[UIView alloc] init];
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];
    
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.size.equalTo(self.view).multipliedBy(0.5);
        make.center.equalTo(self.view);//make centerX and centerY = self.view
    }];
    

是不是Soeasy.....代碼量簡單了很多?

比如我們想實現(xiàn)一個控件 距離其父控件上下左右都為10的效果:

NSInteger padding = 10;
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(superview).with.insets(padding);
}];

是不是爽爆了....
更多更詳細的信息可以去其官方文檔上查看

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市偎捎,隨后出現(xiàn)的幾起案子蠢终,更是在濱河造成了極大的恐慌,老刑警劉巖鸭限,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜕径,死亡現(xiàn)場離奇詭異,居然都是意外死亡败京,警方通過查閱死者的電腦和手機兜喻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赡麦,“玉大人朴皆,你說我怎么就攤上這事》捍猓” “怎么了遂铡?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長晶姊。 經(jīng)常有香客問我扒接,道長,這世上最難降的妖魔是什么们衙? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任钾怔,我火速辦了婚禮,結果婚禮上蒙挑,老公的妹妹穿的比我還像新娘宗侦。我一直安慰自己,他們只是感情好忆蚀,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布矾利。 她就那樣靜靜地躺著姑裂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪男旗。 梳的紋絲不亂的頭發(fā)上舶斧,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天,我揣著相機與錄音剑肯,去河邊找鬼捧毛。 笑死,一個胖子當著我的面吹牛让网,可吹牛的內(nèi)容都是我干的呀忧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼溃睹,長吁一口氣:“原來是場噩夢啊……” “哼而账!你這毒婦竟也來了?” 一聲冷哼從身側響起因篇,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤泞辐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后竞滓,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咐吼,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年商佑,在試婚紗的時候發(fā)現(xiàn)自己被綠了锯茄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡茶没,死狀恐怖肌幽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情抓半,我是刑警寧澤喂急,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站笛求,受9級特大地震影響廊移,放射性物質發(fā)生泄漏。R本人自食惡果不足惜探入,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一狡孔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧新症,春花似錦步氏、人聲如沸响禽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至隆嗅,卻和暖如春界阁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背胖喳。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工泡躯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人丽焊。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓较剃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親技健。 傳聞我的和親對象是個殘疾皇子写穴,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容