iOS學(xué)習(xí)之自動(dòng)布局

Autolayout:

最重要的兩個(gè)概念

  • 約束:對(duì)控件位置和大小的限定條件
  • 參照:對(duì)控件設(shè)置的約束是相對(duì)于哪一個(gè)視圖而言的

自動(dòng)布局的核心計(jì)算公式:

obj1.property1 =(obj2.property2 * multiplier)+ constant value

解釋:obj1的property1屬性等于obj2的property2屬性乘以multiplier(系數(shù))再加constant(常量);

約束的優(yōu)先級(jí):

約束的priority屬性表示約束的優(yōu)先級(jí),取值區(qū)間為[0,1000],默認(rèn)為1000。priority值越大,表示優(yōu)先級(jí)越高,會(huì)優(yōu)先執(zhí)行。優(yōu)先級(jí)低的約束只有在優(yōu)先級(jí)較高的約束失效后才會(huì)執(zhí)行。

警告和錯(cuò)誤:

在使用storyboard和xib設(shè)置約束時(shí),約束有可能會(huì)變成黃色或者紅色。當(dāng)發(fā)生這樣的情況時(shí),就說(shuō)明設(shè)置的約束有問(wèn)題。

  • 如果約束是黃色的,這只是一個(gè)警告,代表控件的frame和設(shè)置的約束不相符,更新frame或者更新約束就可以解決。
  • 如果約束是紅色的,這就代表錯(cuò)誤,約束設(shè)置不完全或者某兩個(gè)約束有沖突,只有約束設(shè)置完整并且沒(méi)有沖突,紅色錯(cuò)誤提示才會(huì)消失。

添加約束的3條規(guī)則:

  • 對(duì)于兩個(gè)同層級(jí)View之間的約束關(guān)系,應(yīng)該添加到它們的父View之上。
同層級(jí)的View
  • 對(duì)于兩個(gè)不同層級(jí)View之間的約束關(guān)系,應(yīng)該添加到它們最近的共同父View上。


    不同層級(jí)的View
  • 對(duì)于有層次關(guān)系的兩個(gè)view之間的約束關(guān)系,添加到層次較高的父view上
有層級(jí)關(guān)系的View

代碼實(shí)現(xiàn)Autolayout:

代碼實(shí)現(xiàn)Autolayout需要注意:

  • 必須先禁止autoresizing功能,設(shè)置View的下面屬性為NO
    view.translatesAutoresizingMaskIntoConstraints = NO;
  • 添加約束之前,一定要保證相關(guān)控件都已經(jīng)添加到各自的父控件上了
  • 不用再給View設(shè)置frame

利用NSLayoutConstraint類創(chuàng)建具體的約束對(duì)象

添加約束到相應(yīng)的View上:

- (void)addConstraint:(NSLayoutConstraint *)constraint;
- (void)addConstraints:(NSArray *)constraints;

NSLayoutConstraint:

一個(gè)NSLayoutConstraint對(duì)象就代表一個(gè)約束,可以通過(guò)修改NSLayoutConstraint對(duì)象的屬性來(lái)修改約束。

創(chuàng)建約束對(duì)象常用的方法:

/**
 *  添加一個(gè)約束,其實(shí)就是根據(jù)公式來(lái)計(jì)算約束
 *  obj1.property1 =(obj2.property2 * multiplier)+ constant value
 *  @param view1      需要添加約束的View
 *  @param attr1      需要添加的約束(左邊、右邊、長(zhǎng)寬還是。。。)
 *  @param relation   約束關(guān)系(大于、等于還是小于)
 *  @param view2      參照View(約束是相對(duì)于哪個(gè)View而言)
 *  @param attr2      參照View的哪一個(gè)參數(shù)(左邊、右邊、長(zhǎng)寬還是。。。)
 *  @param multiplier 系數(shù)
 *  @param c          常量值
 *
 *  @return 返回一個(gè)創(chuàng)建好的約束
 */

+ (instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;

示例:

UIView *blueView = [[UIView alloc] init];
blueView.backgroundColor = [UIColor blueColor];
[self.view addSubview:blueView];

//關(guān)閉Autoresizing
blueView.translatesAutoresizingMaskIntoConstraints = NO;

//創(chuàng)建左邊約束
NSLayoutConstraint *leftLc = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:20];
[self.view addConstraint:leftLc];

//創(chuàng)建右邊約束
NSLayoutConstraint *rightLc = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:-20];
[self.view addConstraint:rightLc];

//創(chuàng)建底部約束
NSLayoutConstraint *bottomLc = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-20];
[self.view addConstraint:bottomLc];

//創(chuàng)建高度約束
NSLayoutConstraint *heightLc = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:50];
[blueView addConstraint: heightLc];

效果圖:


豎屏效果
橫屏效果

使用VFL語(yǔ)句添加約束:

使用VFL來(lái)創(chuàng)建約束數(shù)組:

/**
*  使用VFL語(yǔ)句來(lái)創(chuàng)建約束數(shù)組
*
*  @param format  VFL語(yǔ)句
*  @param opts    約束類型
*  @param metrics VFL語(yǔ)句中用到的具體數(shù)值
*  @param views   VFL語(yǔ)句中用到的控件
*
*  @return 返回創(chuàng)建好的約束數(shù)組
*/
+ (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(nullable NSDictionary<NSString *,id> *)metrics views:(NSDictionary<NSString *, id> *)views;

VFL語(yǔ)句示例:

H:[cancelButton(72)]-12-[acceptButton(50)]
canelButton寬72,acceptButton寬50,它們之間間距12

H:[wideView(>=60@700)]
wideView寬度大于等于60point,該約束條件優(yōu)先級(jí)為700(優(yōu)先級(jí)最大值為1000,優(yōu)先級(jí)越高的約束越先被滿足)

V:[redBox][yellowBox(==redBox)]
豎直方向上,先有一個(gè)redBox,其下方緊接一個(gè)高度等于redBox高度的yellowBox

H:|-10-[Find]-[FindNext]-[FindField(>=20)]-|
水平方向上,F(xiàn)ind距離父view左邊緣默認(rèn)間隔寬度,之后是FindNext距離Find間隔默認(rèn)寬度;再之后是寬度不小于20的FindField,它和FindNext以及父view右邊緣的間距都是默認(rèn)寬度。(豎線“|” 表示superview的邊緣)

VFL代碼示例:

// 創(chuàng)建藍(lán)色View
    UIView *blueView = [[UIView alloc] init];
    blueView.backgroundColor = [UIColor blueColor];
    blueView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:blueView];

    // 創(chuàng)建紅色View
    UIView *redView = [[UIView alloc] init];
    redView.backgroundColor = [UIColor redColor];
    redView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:redView];

    // VFL創(chuàng)建約束
    // 水平方向
    NSString *VFL_H = @"H:|-space-[blueView]-space-[redView(==blueView)]-space-|";
    NSDictionary *metrics = @{
                              @"space" : @30,
                              };
    //    NSDictionary *views = @{
    //                            @"blueView" : blueView,
    //                            @"redView" : redView
    //                            };
    NSDictionary *views = NSDictionaryOfVariableBindings(blueView,redView);

    NSArray *arrayH = [NSLayoutConstraint constraintsWithVisualFormat:VFL_H options:NSLayoutFormatAlignAllTop metrics:metrics views:views];
    [self.view addConstraints:arrayH];

    // 垂直方向
    NSString *VFL_V = @"V:[blueView(50)]-space-|";
    NSArray *arrayV = [NSLayoutConstraint constraintsWithVisualFormat:VFL_V options:kNilOptions metrics:metrics views:views];
    [self.view addConstraints:arrayV];

    // 添加紅色View的約束
    // 添加高度約束
    NSLayoutConstraint *redV_height = [NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:blueView attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0];
    [self.view addConstraint:redV_height];

效果圖:

豎屏效果
橫屏效果

Masonry的使用:

Masonry是目前最流行的Autolayout第三方框架,讓你可以用簡(jiǎn)單的代碼來(lái)編寫(xiě)Autolayout,省去了蘋(píng)果官方繁復(fù)的Autolayout代碼,大大提升了開(kāi)發(fā)效率。

mas_equalToequalTo

默認(rèn)情況下mas_equalTo有自動(dòng)包裝功能,比如自動(dòng)將20包裝為@20。equalTo沒(méi)有自動(dòng)包裝功能。

如果添加了下面的宏,那么mas_equalToequalTo就沒(méi)有區(qū)別:

#define MAS_SHORTHAND_GLOBALS
// 注意:這個(gè)宏一定要添加到#import "Masonry.h"前面

mas_widthwidth:

默認(rèn)情況下
widthmake對(duì)象的一個(gè)屬性,用來(lái)添加寬度約束用的,表示對(duì)寬度進(jìn)行約束。mas_width是一個(gè)屬性值,用來(lái)當(dāng)做equalTo的參數(shù),表示某個(gè)控件的寬度屬性。

如果添加了下面的宏,mas_width也可以寫(xiě)成width:
#define MAS_SHORTHAND

mas_heightmas_centerX以此類推。

下面用Masonry框架實(shí)現(xiàn)一下上面VFL語(yǔ)句的示例,對(duì)比一下就會(huì)發(fā)現(xiàn)有了Masonry框架以后自動(dòng)布局變得多么容易:

//創(chuàng)建藍(lán)色控件
UIView *blueView = [[UIView alloc] init];
blueView.backgroundColor = [UIColor blueColor];
[self.view addSubview:blueView];

//創(chuàng)建紅色控件
UIView *redView = [[UIView alloc] init];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];

// 添加blueView的約束
[blueView makeConstraints:^(MASConstraintMaker *make) {
    make.width.equalTo(redView.width).offset(0);
    make.height.equalTo(redView.height).offset(0);
    make.height.equalTo(100);
    make.left.equalTo(blueView.superview.left).offset(20);
    make.bottom.equalTo(blueView.superview.bottom).offset(-20);
    make.right.equalTo(redView.left).offset(-20);
}];

// 添加redView的約束
[redView makeConstraints:^(MASConstraintMaker *make) {
    make.bottom.equalTo(redView.superview.bottom).offset(-20);
    make.right.equalTo(redView.superview.right).offset(-20);
}];

Masonry框架提供了很多示例程序,感興趣的可以打開(kāi)上面GitHub的鏈接下載下來(lái)仔細(xì)研究,這里就不多寫(xiě)了。
GitHub鏈接為:https://github.com/SnapKit/Masonry

約束動(dòng)畫(huà):

在執(zhí)行動(dòng)畫(huà)時(shí)記得調(diào)用以下方法:

//在修改了約束之后,只要執(zhí)行下面代碼,約束就能做出動(dòng)畫(huà)效果
[UIView animateWithDuration:0.5 animations:^{
      [self.view layoutIfNeeded];
  }];

使用Autolayout實(shí)現(xiàn)UILabel內(nèi)容包裹:

  • 1、設(shè)置UILabel的位置約束
  • 2、設(shè)置label.numberOfLines = 0;,使label能夠自動(dòng)換行來(lái)計(jì)算高度
  • 3、代碼創(chuàng)建約束時(shí),應(yīng)設(shè)置label的最大寬度preferredMaxLayoutWidth。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容