iOS 布局總結(jié)

開發(fā)工作中,有一大部分時(shí)間都是在布局UI界面,可想而知能熟練的布局怎诫,可以很大程度的提高工作效率,如果對(duì)布局知識(shí)掌握不嫻熟贷痪,在布局過程中也會(huì)遇到一些奇葩的問題幻妓,最常見的是約束沖突報(bào)錯(cuò),不知不覺從某個(gè)系統(tǒng)版本開始后就一直出現(xiàn)約束沖突報(bào)錯(cuò)呢诬,很是無奈涌哲,下面是我總結(jié)的一些布局的小知識(shí)點(diǎn)胖缤。

一尚镰、iOS布局的幾種幾種方式:

Frame:

這種布局是性能最高的一種方式,你要看嗎哪廓?我并不打算寫

Autoresizing:

autoresizing是UIView的屬性狗唉,一直都有,使用簡(jiǎn)單涡真,但是沒有autolayout強(qiáng)大分俯,它提供了兩個(gè)屬性肾筐,如下:

@property(nonatomic) BOOL               autoresizesSubviews; // default is YES. if set, subviews are adjusted according to their autoresizingMask if self.bounds changes
@property(nonatomic) UIViewAutoresizing autoresizingMask;    // simple resize. default is UIViewAutoresizingNone

autoresizingMask的枚舉如下:

UIViewAutoresizingNone view的frame不會(huì)隨superview的改變而改變
UIViewAutoresizingFlexibleLeftMargin 自動(dòng)調(diào)整view與superview左邊的距離保證右邊距離不變
UIViewAutoresizingFlexibleWidth 自動(dòng)調(diào)整view的寬,保證與superView的左右邊距不變
UIViewAutoresizingFlexibleRightMargin 自動(dòng)調(diào)整view與superview右邊的距離保證左邊距不變
UIViewAutoresizingFlexibleTopMargin 自動(dòng)調(diào)整view與superview頂部的距離保證底部距離不變
UIViewAutoresizingFlexibleHeight 自動(dòng)調(diào)整view的高缸剪,保證與superView的頂部和底部距離不變
UIViewAutoresizingFlexibleBottomMargin 自動(dòng)調(diào)整view與superview底部部的距離保證頂部距離不變

當(dāng)UIView的autoresizesSubviews是YES時(shí)吗铐,(默認(rèn)是YES), 那么在其中的子view會(huì)根據(jù)子視圖自身的autoresizingMask屬性來自動(dòng)適應(yīng)其與superView之間的位置和大小。也就是說第一個(gè)屬性是決定子視圖能不能autoresize杏节,第二個(gè)屬性是當(dāng)自己的父視圖允許autoresize的情況下來決定自身的布局方式的唬渗,兩者缺一不可。

對(duì)于同一個(gè)父視圖的不同子視圖奋渔,他們之間的相互約束镊逝,autoresize是無法做到的,這時(shí)可以用強(qiáng)大的autoLayout了

【使用注意事項(xiàng)】在使用Xib或者SB時(shí)嫉鲸,Xcode默認(rèn)是autolayout撑蒜。需要取消勾選。在尺寸檢測(cè)器中玄渗,就可以直接設(shè)置座菠。在代碼創(chuàng)建過程中,Xcode默認(rèn)是autoresizing藤树。 直接設(shè)置就好啦辈灼。當(dāng)約束確定后可以修改frame。

AutoLayout:

iOS6之后自動(dòng)布局出來以后也榄,很受歡迎巡莹,為此蘋果還設(shè)計(jì)了VFL可視化語言,但是編碼很麻煩甜紫,于是就出現(xiàn)了鏈?zhǔn)秸Z法的三方庫(kù)masonry降宅,masonry是一個(gè)對(duì)autoLayout的技術(shù)的一個(gè)封裝庫(kù),很受大家歡迎.

【使用注意事項(xiàng)】

  1. 當(dāng)使用autolayout布局完成后獲取到的Frame是不對(duì)的囚霸,可以在DidLayout方法中獲取正確的Frame腰根。
  2. 當(dāng)使用autolayout布局translatesAutoresizingMaskIntoConstraints時(shí)其值應(yīng)該設(shè)置為NO,因?yàn)閍utoresize的布局轉(zhuǎn)換成autolayout的約束的時(shí)候拓型,可能會(huì)與已有的autolayout約束沖突额嘿,如果使用masonry布局則不需要手動(dòng)設(shè)置這個(gè)屬性。

masonry的具體用法就不說了劣挫,GitHub上有詳細(xì)的用法册养,推薦一篇博客Masonry的使用方式

self-sizing:

在iOS 8中压固,蘋果引入了UITableView的一項(xiàng)新功能--Self Sizing Cells球拦,對(duì)于不少開發(fā)者來說這是新SDK中一項(xiàng)非常有用的新功能。在iOS 8之前,如果想在列表視圖中展示可變高度的動(dòng)態(tài)內(nèi)容時(shí)坎炼,你需要手動(dòng)計(jì)算行高愧膀,而Self Sizing Cells為展示動(dòng)態(tài)內(nèi)容提供了一個(gè)解決方案。以下是你使用Self Sizing Cells時(shí)需要注意的事項(xiàng):

  1. 為原型單元格定義Auto Layout約束
  2. 指定表視圖的estimatedRowHeight
  3. 將表視圖的rowHeight屬性設(shè)置為UITableViewAutomaticDimension

如果用代碼表示最后兩點(diǎn)谣光,那就是

tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension

僅有兩行代碼檩淋,你通知表視圖計(jì)算單元格的尺寸以匹配內(nèi)容和和動(dòng)態(tài)進(jìn)行渲染。Self Sizing Cells功能可以為你節(jié)省大量寫代碼的時(shí)間萄金。

【注意】不用Auto Layout布局Self Sizing Cells功能也是無法工作的狼钮,它依賴約束來確定合適的行高。事實(shí)上捡絮,表視圖會(huì)調(diào)用systemLayoutSizeFittingSize并返回基于約束布局下的單元格的尺寸熬芜,具體原理看

如果你是頭一次使用Auto Layout福稳,推薦你先看下Auto Layout Introduction涎拉。

二、幾點(diǎn)值得注意的布局技巧:

  1. 開發(fā)中最常用的還是masrony的圆,其提供的一個(gè)利于debug約束問題的方法鼓拧,代碼摘自masonry的demo上的,我加了注釋
  
    UIView *greenView = UIView.new;
    greenView.backgroundColor = UIColor.greenColor;
    [self addSubview:greenView];

    UIView *redView = UIView.new;
    redView.backgroundColor = UIColor.redColor;
    [self addSubview:redView];

    UILabel *blueView = UILabel.new;
    blueView.backgroundColor = UIColor.blueColor;
    [self addSubview:blueView];

    UIView *superview = self;
    int padding = 10;
    // 給視圖添加key的方式有兩種越妈,如下所示

    // 法一:
    //you can attach debug keys to views like so:
    // greenView.mas_key = @"greenView";
    // redView.mas_key = @"redView";
    // blueView.mas_key = @"blueView";
    // superview.mas_key = @"superview";

    // 法二:
    //OR you can attach keys automagically like so:
    MASAttachKeys(greenView, redView, blueView, superview);

    // 給約束添加key
    [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        //you can also attach debug keys to constaints
        make.edges.equalTo(@1).key(@"ConflictingConstraint"); //composite constraint keys will be indexed
        make.height.greaterThanOrEqualTo(@5000).key(@"ConstantConstraint");

        make.top.equalTo(greenView.mas_bottom).offset(padding);
        make.left.equalTo(superview.mas_left).offset(padding);
        make.bottom.equalTo(superview.mas_bottom).offset(-padding).key(@"BottomConstraint");
        make.right.equalTo(superview.mas_right).offset(-padding);
        make.height.equalTo(greenView.mas_height);
        make.height.equalTo(redView.mas_height).key(@340954); //anything can be a key
    }];
    
    return self;
}

上面的約束有問題季俩,會(huì)拋出問題,如果不給視圖和約束設(shè)置key的話梅掠,xcode提示錯(cuò)誤如下:

Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<MASLayoutConstraint:0x6000000a5e20 UILabel:0x7fb318c2c990.left == MASExampleDebuggingView:0x7fb318c15c00.left + 1>",
    "<MASLayoutConstraint:0x6080000a40e0 UILabel:0x7fb318c2c990.left == MASExampleDebuggingView:0x7fb318c15c00.left + 10>"
)

給視圖和約束添加了key之后的提示如下:

Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<MASLayoutConstraint:ConflictingConstraint[0] UILabel:blueView.left == MASExampleDebuggingView:superview.left + 1>",
    "<MASLayoutConstraint:0x6000000b7ac0 UILabel:blueView.left == MASExampleDebuggingView:superview.left + 10>"
)

是不是看著爽多了酌住。。
這樣你就能看到具體是哪個(gè)view的哪個(gè)約束可能出現(xiàn)問題了阎抒,而不是出現(xiàn)一堆的十六進(jìn)制地址

  1. 添加或者更新(update酪我、remake)約束的代碼應(yīng)該放在哪,代碼如下一看便知
// 當(dāng)你使用autoLayout布局的時(shí)候建議寫此方法且叁,防止autoresize布局造成的錯(cuò)誤
+ (BOOL)requiresConstraintBasedLayout
{
    return YES;
}
// this is Apple's recommended place for adding/updating constraints
// 蘋果推薦添加或者更新(update都哭、remake)約束的地方
- (void)updateConstraints {

    [self.growingButton updateConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self);
        make.width.equalTo(@(self.buttonSize.width)).priorityLow();
        make.height.equalTo(@(self.buttonSize.height)).priorityLow();
        make.width.lessThanOrEqualTo(self);
        make.height.lessThanOrEqualTo(self);
    }];
    // according to apple super should be called at end of method
    [super updateConstraints];
}
  1. autoLayout不允許對(duì)其屬性例如左、右逞带、centerY等被設(shè)置為常量欺矫,因此如果你要給這些屬性傳遞一個(gè)NSNumber類型的值得時(shí)候masonry會(huì)將他們轉(zhuǎn)換成與父視圖相關(guān)的約束。

However Auto Layout does not allow alignment attributes such as left, right, centerY etc to be set to constant values. 
So if you pass a NSNumber for these attributes
 Masonry will turn these into constraints relative to the view’s superview ie:
 [view makeConstraints:^(MASConstraintMaker *make) {    
        make.left.lessThanOrEqualTo(@10)
 }];

view的左邊距等價(jià)于 view.left = view.superview.left + 10

  1. 按比例布局展氓,如果各占一半的話穆趴,也可以不用multipliedBy,直接約束兩個(gè)視圖的width isEqual就行了
// topInnerView的寬度是高度的1/3
[self.topInnerView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.width.equalTo(self.topInnerView.mas_height).multipliedBy(3);
]
  1. 你用NSAutoLyoutConstraints布局的時(shí)候需要設(shè)置視圖的view1.translatesAutoresizingMaskIntoConstraints = NO带饱,默認(rèn)值是YES毡代,等于YES的時(shí)候autoresize會(huì)影響autolayout布局阅羹,有時(shí)會(huì)發(fā)現(xiàn)效果不是自己想要的勺疼,不過如果你用masonry設(shè)置約束的時(shí)候教寂,masonry會(huì)幫你把這個(gè)屬性值設(shè)置為NO,你不用管它执庐,寫出來就是想提醒你酪耕。

  2. 看完官方的demo,發(fā)現(xiàn)他們會(huì)把需要的每個(gè)約束都寫上轨淌,但是有時(shí)候不需要全寫上迂烁,如下面被我注釋的代碼,但是官方是沒有注釋的递鹉,既然人家官方都這樣寫盟步,你是不是也應(yīng)該這樣寫啊,別注釋了躏结,這樣不容易出錯(cuò)却盘,如下:

 UIView *superview = self;
    int padding = 10;
    //if you want to use Masonry without the mas_ prefix
    //define MAS_SHORTHAND before importing Masonry.h see Masonry iOS Examples-Prefix.pch
    [greenView makeConstraints:^(MASConstraintMaker *make) {
        make.top.greaterThanOrEqualTo(superview.top).offset(padding);
        make.left.equalTo(superview.left).offset(padding);
        make.bottom.equalTo(blueView.top).offset(-padding);
        make.right.equalTo(redView.left).offset(-padding);
        make.width.equalTo(redView).multipliedBy(1);

        make.height.equalTo(redView.height);
        make.height.equalTo(blueView.height);
    }];

    //with is semantic and option
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(superview.mas_top).with.offset(padding); //with with
        //make.left.equalTo(greenView.mas_right).offset(padding); //without with
        make.bottom.equalTo(blueView.mas_top).offset(-padding);
        make.right.equalTo(superview.mas_right).offset(-padding);
        // make.width.equalTo(greenView).multipliedBy(1);
        
        make.height.equalTo(@[greenView, blueView]); //can pass array of views
    }];
    
    [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(greenView.mas_bottom).offset(padding);
        make.left.equalTo(superview.mas_left).offset(padding);
        make.bottom.equalTo(superview.mas_bottom).offset(-padding);
        make.right.equalTo(superview.mas_right).offset(-padding);
        make.height.equalTo(@[greenView.mas_height, redView.mas_height]); //can pass array of attributes
    }];
    return self;
}

要注意blueView設(shè)置高度依賴的時(shí)候設(shè)置的是一個(gè)數(shù)組這樣的用法

  1. masonry動(dòng)畫

@implementation MASExampleUpdateView

- (id)init {
    self = [super init];
    if (!self) return nil;

    self.growingButton = [UIButton buttonWithType:UIButtonTypeSystem];
    [self.growingButton setTitle:@"Grow Me!" forState:UIControlStateNormal];
    self.growingButton.layer.borderColor = UIColor.greenColor.CGColor;
    self.growingButton.layer.borderWidth = 3;
    [self.growingButton addTarget:self action:@selector(didTapGrowButton:) forControlEvents:UIControlEventTouchUpInside];
    [self addSubview:self.growingButton];
    self.buttonSize = CGSizeMake(100, 100);
    return self;
}

+ (BOOL)requiresConstraintBasedLayout
{
    return YES;
}

// this is Apple's recommended place for adding/updating constraints
// 蘋果推薦添加或或者更新約束的地方
- (void)updateConstraints {

    [self.growingButton updateConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self);
        make.width.equalTo(@(self.buttonSize.width)).priorityLow();
        make.height.equalTo(@(self.buttonSize.height)).priorityLow();
        make.width.lessThanOrEqualTo(self);
        make.height.lessThanOrEqualTo(self);
    }];
    
    // according to apple super should be called at end of method
    [super updateConstraints];
}

- (void)didTapGrowButton:(UIButton *)button {
    self.buttonSize = CGSizeMake(self.buttonSize.width * 1.3, self.buttonSize.height * 1.3);

    // tell constraints they need updating
    // 告訴約束系統(tǒng)要更新,系統(tǒng)會(huì)調(diào)用上面重寫的updateConstraints方法
    [self setNeedsUpdateConstraints];

    // update constraints now so we can animate the change,
    // it will be call by system automatically
    // 該方法不必手動(dòng)調(diào)用
    // [self updateConstraintsIfNeeded];

    // 可以用layoutIfNeeded來實(shí)現(xiàn)即時(shí)更新媳拴,還可以添加動(dòng)畫
    [UIView animateWithDuration:0.4 animations:^{
        [self layoutIfNeeded]; // 需要在此處調(diào)用layoutIfNeeded方法才能產(chǎn)生動(dòng)畫
    }];
}
@end
  1. masonry 布局scrollVIew(垂直滾動(dòng)的情況下)黄橘;

偽代碼如下

[self.view addSubView:self.scrollView];
[scrollView addSubView:self.subViews];

[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.edges.equalTo(self.view);
     make.bottom.equalTo(self.subViews); // 此處要注意
}];

[self.subViews mas_makeConstraints:^(MASConstraintMaker *make) {
      make.top.equalTo(self.scrollView); //此處注意
      make.left.right.equalTo(self.view); // 此處注意
      make.height.equalTo(@(1000));
}];

對(duì)于垂直滑動(dòng)的scrollView來說,top屈溉、left塞关、right的邊距要等于self.view的邊距,但是bottom要依賴于子視圖子巾。
對(duì)于處于scrollView的子視圖來說帆赢,leftright的邊距要等于self.view线梗,top匿醒、bottom的邊距要依賴于scrollView

  1. 布局水平方向兩個(gè)寬度不固定的視圖,詳情
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末缠导,一起剝皮案震驚了整個(gè)濱河市廉羔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌僻造,老刑警劉巖憋他,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異髓削,居然都是意外死亡竹挡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門立膛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來揪罕,“玉大人梯码,你說我怎么就攤上這事『脝” “怎么了轩娶?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)框往。 經(jīng)常有香客問我鳄抒,道長(zhǎng),這世上最難降的妖魔是什么椰弊? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任许溅,我火速辦了婚禮,結(jié)果婚禮上秉版,老公的妹妹穿的比我還像新娘贤重。我一直安慰自己,他們只是感情好清焕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布并蝗。 她就那樣靜靜地躺著,像睡著了一般耐朴。 火紅的嫁衣襯著肌膚如雪借卧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天筛峭,我揣著相機(jī)與錄音铐刘,去河邊找鬼。 笑死影晓,一個(gè)胖子當(dāng)著我的面吹牛镰吵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播挂签,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼疤祭,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了饵婆?” 一聲冷哼從身側(cè)響起勺馆,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎侨核,沒想到半個(gè)月后草穆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡搓译,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年悲柱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片些己。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡豌鸡,死狀恐怖嘿般,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情涯冠,我是刑警寧澤炉奴,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站功偿,受9級(jí)特大地震影響盆佣,放射性物質(zhì)發(fā)生泄漏往堡。R本人自食惡果不足惜械荷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望虑灰。 院中可真熱鬧吨瞎,春花似錦、人聲如沸穆咐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽对湃。三九已至崖叫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拍柒,已是汗流浹背心傀。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拆讯,地道東北人脂男。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像种呐,于是被迫代替她去往敵國(guó)和親宰翅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

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