你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 有趣的Autolayout示例

有趣的Autolayout示例

編輯:IOS開發基礎

前言

好久沒有寫Blog了,這段時間有點忙。

本文舉了3個比較有“特點”的Autolayout例子,源於微博上好友的提問,感覺比較有意思,也比較有代表性,就寫了出來,分享給大家~

至於為什麼用Masonry,那是因為它好用啊!(被問到過有關Masonry的問題,就索性用它來實現吧=。=)。

效果圖

1.gif

Github地址

https://github.com/zekunyan/AutolayoutExampleWithMasonry

關於例子工程結構

實現的時候采用的是用StoryBoard拖拽約束+Masonry手寫代碼相結合的方式實現。最關鍵的地方是用Masonry,為了更好地突出重點。其它的無關緊要的空間約束,直接就拖拽了。

關於Autolayout

剛開始學習Autolayout的時候,什麼“Leading Edges”、“Horizontal Centers”,好多啊,感覺一下子適應不來,有時候面對一個界面布局上的需求,可能都無從下手。

總的來說,我覺得Autolayout的關鍵就是“Constraint(約束)”。其實就是一下兩點:

  1. 從顯式設置frame的屬性,到利用約束控制View的大小、位置。

  2. 思考如何布局時,重點從單個的View,到整體所有View之間的相互關系。

既然沒有了具體設置View的frame屬性,也就是說,系統會在運行時,通過我們設定的“約束”,計算出每個View的frame,再去繪制屏幕內容。

也就是說,我們設置的Constraint,要能體現出View的位置(x、y坐標)、大小(寬高)。無論是用IB拖拽約束,還是手寫代碼,只要從這個角度去思考,很多問題就都能解決。

有關Autolayout的知識,網上有很多,在這裡就不詳細列出了,但是有個公式倒是可以貼出來:

viewA-attribute = viewB-attribute * multiplier + constant

關於Masonry

好用!

Case 1: 並排兩個label,寬度由內容決定。父級View寬度不夠時,優先顯示左邊label的內容

遇到這種跟內容壓縮、優先級有關的布局,就不得不提Autolayout中的兩個重要的屬性“Content Compression Resistance”和“Content Hugging”。

Content Compression Resistance = 不許擠我!

對,這個屬性說白了就是“不許擠我”=。=

這個屬性的優先級(Priority)越高,越不“容易”被壓縮。也就是說,當整體的空間裝不小所有的View的時候,Content Compression Resistance優先級越高的,現實的內容越完整。

Content Hugging = 抱緊!

這個屬性的優先級越高,整個View就要越“抱緊”View裡面的內容。也就是View的大小不會隨著父級View的擴大而擴大。

分析

根據要求,可以將約束分為兩個部分:

  1. 整體空間足夠時,兩個label的寬度由內容決定,也就是說,label的“Content Hugging”優先級很高,而且沒有固定的Width屬性。

  2. 整體空間不夠時,左邊的label更不容易被壓縮,也就是“Content Compression Resistance”優先級更高。

重點:

  1. label不設置具體的寬度(width)屬性,寬度由內容決定。

  2. 顯示的優先級由“Content Compression Resistance”屬性的高低決定。

約束示例圖

2.jpg

關鍵代碼

關鍵的代碼如下:(label1是左邊的label,label2是右邊的)

設置位置

// label1: 位於左上角
[_label1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(_contentView1.mas_top).with.offset(5);
    make.left.equalTo(_contentView1.mas_left).with.offset(2);
    // 40高度
    make.height.equalTo(@40);
}];
// label2: 位於右上角
[_label2 mas_makeConstraints:^(MASConstraintMaker *make) {
    //左邊貼著label1,間隔2
    make.left.equalTo(_label1.mas_right).with.offset(2);
    //上邊貼著父view,間隔5
    make.top.equalTo(_contentView1.mas_top).with.offset(5);
    //右邊的間隔保持大於等於2,注意是lessThanOrEqual
    //這裡的“lessThanOrEqualTo”放在從左往右的X軸上考慮會更好理解。
    //即:label2的右邊界的X坐標值“小於等於”containView的右邊界的X坐標值。
    make.right.lessThanOrEqualTo(_contentView1.mas_right).with.offset(-2);
    //只設置高度40
    make.height.equalTo(@40);
}];

設置內容約束

//設置label1的content hugging 為1000
[_label1 setContentHuggingPriority:UILayoutPriorityRequired
                           forAxis:UILayoutConstraintAxisHorizontal];
//設置label1的content compression 為1000
[_label1 setContentCompressionResistancePriority:UILayoutPriorityRequired
                                         forAxis:UILayoutConstraintAxisHorizontal];
//設置右邊的label2的content hugging 為1000
[_label2 setContentHuggingPriority:UILayoutPriorityRequired
                           forAxis:UILayoutConstraintAxisHorizontal];
//設置右邊的label2的content compression 為250
[_label2 setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
                                         forAxis:UILayoutConstraintAxisHorizontal];

小節

靈活運用“Content Compression Resistance”和“Content Hugging”屬性。

Case 2: 四個ImageView整體居中,可以任意顯示、隱藏

先看看示例的截圖:

blob.png

下面的四個Switch控件分別控制上面對應位置的圖片是否顯示。

分析

  1. 首先就是整體居中,為了實現這個,最簡單的辦法就是將四個圖片“裝進”一個容器View裡面,然後讓這個容器View在整個頁面中居中即可。這樣就不用控制每個圖片的居中效果了。

  2. 然後就是顯示與隱藏。在這裡我直接控制圖片ImageView的寬度,寬度為0的時候不就“隱藏”了嗎。

約束示例圖

4.jpg

解釋

之所以這麼設置,主要目的有以下幾點:

  1. 盡量減少無效的約束,保證約束不多也不少。

  2. 內部的每個imageView約束其實都只有四個:left、centerY、width和height,這樣有個好處,就是可以寫個函數,專門為View一次性添加這幾個約束,大大減少代碼量。

  3. 最右邊的imageView還要單獨設置跟容器View的右邊約束,是為了不用設置容器View的width,保證容器View是剛好包含內部的View的,這樣整體才是居中的。

關鍵代碼

先看看設置每個imageView約束的函數:

/**
*  設置view的寬高、左邊約束,垂直中心約束
*
*  @param view    要設置的view
*  @param size    CGSize
*  @param left    左邊對齊的約束
*  @param centerY 垂直中心對齊的約束
*
*  @return 返回寬約束,用於顯示、隱藏單個view
*/
- (MASConstraint *)setView:(UIView *)view size:(CGSize)size left:(MASViewAttribute *)left centerY:(MASViewAttribute *)centerY {
    __block MASConstraint *widthConstraint;
    [view mas_makeConstraints:^(MASConstraintMaker *make) {
        //寬高固定
        widthConstraint = make.width.equalTo(@(size.width));
        make.height.equalTo(@(size.height));
        //左邊約束
        make.left.equalTo(left);
        //垂直中心對齊
        make.centerY.equalTo(centerY);
    }];
    return widthConstraint;
}

接著就是設置容器View的代碼:

//containerView 就是 容器View
[_containerView mas_makeConstraints:^(MASConstraintMaker *make) {
    //只設置高度,寬度由子View決定
    make.height.equalTo(@(IMAGE_SIZE));
    //水平居中
    make.centerX.equalTo(self.view.mas_centerX);
    //距離父View頂部200點
    make.top.equalTo(self.view.mas_top).offset(200);
}];

最後是內部imageView的約束:

//分別設置每個imageView的寬高、左邊、垂直中心約束,注意約束的對象
//每個View的左邊約束和左邊的View的右邊相等=。=,有點繞口...
UIImageView *imageView1 = _imageViews[0];
MASConstraint *width = [self setView:imageView1 size:imageViewSize left:_containerView.mas_left centerY:_containerView.mas_centerY];
[_widthConstraints addObject:width];
UIImageView *imageView2 = _imageViews[1];
width = [self setView:imageView2 size:imageViewSize left:imageView1.mas_right centerY:_containerView.mas_centerY];
[_widthConstraints addObject:width];
UIImageView *imageView3 = _imageViews[2];
width = [self setView:imageView3 size:imageViewSize left:imageView2.mas_right centerY:_containerView.mas_centerY];
[_widthConstraints addObject:width];
UIImageView *imageView4 = _imageViews[3];
width = [self setView:imageView4 size:imageViewSize left:imageView3.mas_right centerY:_containerView.mas_centerY];
[_widthConstraints addObject:width];
//最後設置最右邊的imageView的右邊與父view的最有對齊
[imageView4 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.right.equalTo(_containerView.mas_right);
}];

控制ImageView顯示、隱藏的時候,直接讓其寬度等於0就行:

- (IBAction)showOrHideImage:(UISwitch *)sender {
    NSUInteger index = (NSUInteger) sender.tag;
    MASConstraint *width = _widthConstraints[index];
    if (sender.on) {
        width.equalTo(@(IMAGE_SIZE));
    } else {
        width.equalTo(@0);
    }
}

小節

有時候用個“容器View”管理內部的View,往往會起到事半功倍的效果。而且在組織約束的時候,盡量的將約束統一起來,這樣可以用一個函數去設置,減少代碼量。

Case 3: 子View的寬度始終是父級View的一半(或者任意百分比)

其實這個很簡單=。= 再看看這個公式:

viewA-attribute = viewB-attribute * multiplier + constant

這個是Autolayout裡面一個約束的不同屬性的基本組合關系,替換成寬度的話,就是下面這樣:

子View的寬度 = 父級View寬度 * 系數 + 常數;

在Masonry裡面,其實有個函數“multipliedBy”,就是用來設置multipler屬性的(跟原本的NSLayoutConstraint的對應)。

關鍵代碼

如下:

[subView mas_makeConstraints:^(MASConstraintMaker *make) {
    //上下左貼邊
    make.left.equalTo(_containerView.mas_left);
    make.top.equalTo(_containerView.mas_top);
    make.bottom.equalTo(_containerView.mas_bottom);
    //寬度為父view的寬度的一半
    make.width.equalTo(_containerView.mas_width).multipliedBy(0.5);
}];

接著,只要控制父級View的寬度,子View的寬度就會隨著變化了。

小節

multipliedBy在Masonry的Github主頁裡面沒有=。=

所以要養成讀頭文件的習慣~

總結

有關Autolayout的東西還有好多沒有寫,什麼動畫啊、動態修改約束之類的,本文也算是個引子吧,任重而道遠~

能看到這的朋友,也算是很有耐心了,哈哈~~

參考

  • SnapKit/Masonry

  • Masonry介紹與使用實踐(快速上手Autolayout)

  • AutoLayout:忘掉Frame,擁抱Constraint

  • Autolayout 基礎

  • 對Auto Layout中的Content Compression Resistance和Content Hugging的總結

  • Auto Layout Guide

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved