閱讀68 返回首頁    go 阿裏雲 go 技術社區[雲棲]


AutoLayout全解

AutoLayout簡介

Autolayout是一種全新的布局技術,專門用來布局UI界麵的,用來取代Frame布局在遇見屏幕尺寸多重多樣的問題。Autolayout自iOS 6開始引入,但是由於Xcode 4的不給力,當時並沒有得到大規模推廣。在iOS 7(Xcode5)開始,Autolayout的開發效率得到很大的提升,蘋果官方也推薦開發者盡量使用Autolayout來布局UI界麵,減少純代碼的方式。
那麼AutoLayout怎麼使用呢?
這裏寫圖片描述

VFL

VFL(Virsual Format Language)是一種虛擬的格式化語言,主要用來創建AutoLayout的約束字符串。
示例,如:V: |-(0)-Label1-(0)-Label2-(0)-| 方向:從左到右,從上到下
V:表示方向為垂直方向,也就是豎向;H為橫向。
|:豎線表示為邊界(當前所在View的邊界),這裏緊鄰方向表示符V,方向是從上到下,因此表示上麵界。
0:NSNumber 0 表示約束值為0。這裏是Label1距離上邊界的約束為0。
Label1:表示對象Label1。
0:表示Label1和Label2的約束為0.
Label2:表示對象Label2。
0:表示Label2和下邊界的約束為0.
|:表示下邊界。

關於[VFL官網]詳細知識,請查看官方的介紹

AutoLayout IB使用方式

為了讓布局能夠在不同屏幕的size上都能夠表現正常,我們需要對其增加“約束”。然後,在不同屏幕尺寸下view就能夠按照約束來局。
這裏寫圖片描述

這裏寫圖片描述

添加如下約束:
這裏寫圖片描述

屬性說明:
1:距離邊緣 最上麵的4個虛線表示某個View的距離上邊 左邊 右邊 下邊多高
2:那個藍色的Constrain To Margins 是iPhone6出現之後。
Apple 覺得更大的分辨率有點間距好看, 默認為8 , 如果這個勾上了 這個View距離四周的值就變成了 你輸入的值+8。 一般建議勾掉 。

案例1

設置某個View距離父View上下左右間距全部為20。
這裏寫圖片描述

案例2

某個View距離父View的左側20,上20,寬高均為100。
這裏寫圖片描述

注意:我在添加約束的時候有個選項叫做updateFrame 如果勾選 會直接將Frame調整到真實值 ,而不需要再次update 。

案例3

某個View距離在父View的左側20 案例2中白色View 上20 寬高和Demo2中的寬高一樣。
這裏寫圖片描述

然後,點擊某個約束。
這裏寫圖片描述

對其處理

這裏寫圖片描述

屬性說明:

  • Leading Edges:左對齊
  • Trailing Edges:右對齊
  • Top Edges:上對齊
  • Bottom Edges:下對齊
  • Horizontal Centers:水平中心對齊
  • Vertical Centers:豎向中心對齊
  • Baselines:基線對齊
  • Horizontal Center in Container:對齊容器中的水平中心
  • Vertical Center in Container:對齊容器中的豎向中心

案例4

某個View距離在父View的右側20 案例3中白色View上20 寬高和案例3中的寬高一樣 並且對齊。
這裏寫圖片描述

案例5

某個label和另外一個label基線對齊。
這裏寫圖片描述

像label 默認是有寬度的 寬度就是字體自適應的。這樣我們就可以不給UIlabel 高度 把Label的NumberOfline = 0就可以自適應高度了。
這裏寫圖片描述

Tip

1,有時候約束太多的時候 我們可以給某個View起個假名字以起到唯一標識的作用。
2,View總是選不中怎麼辦?按 ctrl + shift + 單擊。
3,當ScrollView過長無法編輯怎麼辦?將控製器改為Freedom 修改ContentView的高度約束 這樣ScrollView 就可以滾動了。
這裏寫圖片描述

UItableViewCell高度計算

為了方便說明,本部分知識主要從以下幾個方麵講解。

  • AutoLayout with UILabel in UITableViewCell
  • AutoLayout with UITextView in UITableViewCell
  • Manual Layout with UILabel in UITableViewCell
  • Manual Layout with UITextView in UITableViewCell
  • 隨UITextView高度動態改變Cell高度

AutoLayout with UILabel

創建一個空的xib,命名為C1.xib, 然後拖入一個UITableViewCell控件。接著創建一個UITableViewCell的子類,命名為C1類。然後在C1.xib中,將與C1類進行關聯。隻需要在Class那裏寫入關聯的類名C1即可。
這裏寫圖片描述
還有由於UITableViewCell需要重用功能,所以我們還需要設置一個重用標識。
這裏寫圖片描述

下麵是使用autoLayout的布局。
這裏寫圖片描述

接著我們在UITableView中來使用我們自定義的UITableViewCell C1。首先我們創建一個UITableViewController的子類T1ViewController, 接著在Main.storyboard中拖入一個UITableViewController,並關聯T1ViewController。
這裏寫圖片描述

Auto Layout with UITextView

同樣參考上麵我們創建一個C2.xib, UITableViewCell的子類C2,並關聯C2.xib與C2類。並在C2.xib中對其布局,同樣使用了auto layout. 布局如下圖:
這裏寫圖片描述
創始UITableViewController的了類T2ViewController,在Main.storyboard中拖入UITableViewController,並關聯他們。接著代碼中注冊C2.xib到UITableView。
如下麵是計算UITableView高度的代碼:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    C2 *cell = (C2 *)self.prototypeCell;
    cell.t.text = [self.tableData objectAtIndex:indexPath.row];
    CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
    CGSize textViewSize = [cell.t sizeThatFits:CGSizeMake(cell.t.frame.size.width, FLT_MAX)];
    CGFloat h = size.height + textViewSize.height;
    h = h > 89 ? h : 89;  //89是圖片顯示的最低高度, 見xib
    NSLog(@"h=%f", h);
    return 1 + h;
}

在這兒我們是通過sizeThatFits:計算的UITextView的高度,然後加上systemLayoutSizeFittingSize:返回的高度。為什麼要這樣呢? 因為UITextView內容的高度不會影響systemLayoutSizeFittingSize計算。
下麵是UITextView的實例:
這裏寫圖片描述
此圖中距頂的約束是10, 距底的約束8, 距左邊約束是87,距右邊的約束是13, 那麼systemLayoutSizeFittingSize:返回的CGSize為height等於19, size等於100. 它UITextView的frame是不影響systemLayoutSizeFittingSize:的計算。所以,我們需要加上textViewSize.height。

Manual Layout with UILabel

按照前麵介紹的,我們需要創建C3.xib, C3類, T3ViewController類,Main.storyboard中拖入UITableViewController,並分別建立關聯。 為了簡單,C3.xib中我就不加padding之類的了,如圖:
這裏寫圖片描述
然後添加如下的計算代碼:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    C3 *cell = [self.tableView dequeueReusableCellWithIdentifier:@"C3"];
    cell.t.text = [self.tableData objectAtIndex:indexPath.row];
    [cell.t sizeToFit];
    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    C3 *cell = (C3 *)self.prototypeCell;
    NSString *str = [self.tableData objectAtIndex:indexPath.row];
    cell.t.text = str;
    CGSize s = [str calculateSize:CGSizeMake(cell.t.frame.size.width, FLT_MAX) font:cell.t.font];
    CGFloat defaultHeight = cell.contentView.frame.size.height;
    CGFloat height = s.height > defaultHeight ? s.height : defaultHeight;
    NSLog(@"h=%f", height);
    return 1  + height;
}

這裏,用到了一個NSString的Cagetory方法,代碼如下:

- (CGSize)calculateSize:(CGSize)size font:(UIFont *)font 
{
    CGSize expectedLabelSize = CGSizeZero;

    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
        NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
        paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
        NSDictionary *attributes = @{NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle.copy};

        expectedLabelSize = [self boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size;
    }
    else {
        expectedLabelSize = [self sizeWithFont:font
                                       constrainedToSize:size
                                           lineBreakMode:NSLineBreakByWordWrapping];
    }

    return CGSizeMake(ceil(expectedLabelSize.width), ceil(expectedLabelSize.height));
}

運行效果如下:
這裏寫圖片描述

Manual Layout with UITextView

按照前麵介紹的,我們需要創建C4.xib, C4類, T4ViewController類,Main.storyboard中拖入UITableViewController,並分別建立關聯。 為了簡單,C4.xib中我就不加padding之類的了,如圖:
這裏寫圖片描述
相關代碼如下:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    C4 *cell = [self.tableView dequeueReusableCellWithIdentifier:@"C4"];
    cell.t.text = [self.tableData objectAtIndex:indexPath.row];
    [cell.t sizeToFit];
    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    C4 *cell = (C4 *)self.prototypeCell;
    NSString *str = [self.tableData objectAtIndex:indexPath.row];
    cell.t.text = str;
    CGSize s =  [cell.t sizeThatFits:CGSizeMake(cell.t.frame.size.width, FLT_MAX)];
    CGFloat defaultHeight = cell.contentView.frame.size.height;
    CGFloat height = s.height > defaultHeight ? s.height : defaultHeight;
    return 1  + height;
}

隨UITextView高度改變Cell高度

當UITextView內容改變的時候,計算自身高度,然後通知UITableView更新,這樣就會觸發UITableViewCell高度重新計算,從而改變Cell的高度。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    C5 *cell = [self.tableView dequeueReusableCellWithIdentifier:@"C5"];
    cell.t.text = @"123";
    cell.t.delegate = self;
    return cell;
}

#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    C5 *cell = (C5 *)self.prototypeCell;
    cell.t.text = self.updatedStr;
    CGSize s =  [cell.t sizeThatFits:CGSizeMake(cell.t.frame.size.width, FLT_MAX)];
    CGFloat defaultHeight = cell.contentView.frame.size.height;
    CGFloat height = s.height > defaultHeight ? s.height : defaultHeight;
    return 1  + height;
}

#pragma mark - UITextViewDelegate
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    if ([text isEqualToString:@"\n"]) {
        NSLog(@"h=%f", textView.contentSize.height);
    }
    return YES;
}

- (void)textViewDidChange:(UITextView *)textView {
    self.updatedStr = textView.text;
    [self.tableView beginUpdates];
    [self.tableView endUpdates];
}

關於UITableViewCell使用自動布局的優化可以查看下麵的介紹:
優化UITableViewCell高度計算

StackView

UIStackView是iOS9新引入的控件,它支持垂直和水平排列多個子視圖(SubView)。例如:水平放置三個按鈕,等寬,並且按鈕間的間隙為10,如果自己實現會比較麻煩,而使用UIStackView則很容易實現。UIStackView目前隻支持iOS9+版本,如果要在iOS 7版本上使用UIStackView,可以使用下麵兩個第三方庫:OAStackViewTZStackView。其中:

  • OAStackView,基於OC的StackView庫,支持iOS7+以上的係統,同時支持代碼和IB視圖。
  • TZStackView,基於Swift的StackView庫,同樣支持iOS7+以上的係統,但是不支持storyboard。

OAStackView實現子視圖等分

案例1

例如,下麵的例子是使用OAStackView實現視圖等分的例子。
這裏寫圖片描述
相關代碼如下:

 UILabel *l1 = [[UILabel alloc] init];
 l1.text = @"Label 1";
 UILabel *l2 = [[UILabel alloc] init];
 l2.text = @"Label 2";
 OAStackView *stackView = [[OAStackView alloc] initWithArrangedSubviews:@[l1, l2]];
 stackView.translatesAutoresizingMaskIntoConstraints = NO;
 [self.view addSubview:stackView];
 [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-30-[stackView]"
                                                                  options:0
                                                                  metrics:0
                                                                    views:NSDictionaryOfVariableBindings(stackView)]];
 [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-10-[stackView]"
                                                                  options:0
                                                                  metrics:0
                                                                    views:NSDictionaryOfVariableBindings(stackView)]];

上麵的例子實現了兩個按鈕的垂直排列。如果想要水平排列,修改stackView.axis值為UILayoutConstraintAxisHorizontal即可。需要注意,因為不是用IB創建的View,所以要設定View的translatesAutoresizingMaskIntoConstraints屬性為NO,否則排列屬性不生效。當非IB創建時,屬性默認為YES;當IB創建View時,屬性默認為NO。

案例2

在水平方向上放4張圖片,圖片等分。
1,首先在頁麵上拖拽1個imageView,將它的寬高都設置成50。
這裏寫圖片描述
2,然後再添加三個imageView,將界麵上的四個ImageView設置等寬等高。
這裏寫圖片描述
3,然後再將他們加入到Stack View中,設置Stack View 的distribution屬性為 Equal Spacing(等間距)。
這裏寫圖片描述

這裏寫圖片描述

4,最後,設置Stack View 的位置
這裏寫圖片描述

這裏寫圖片描述

StackView屬性

在理解StackView時,有幾個屬性需要理解:
Axis: 這個屬性是改變UIStackView中的排布方式的屬性,其中有水平排布與垂直排布

  • Alignment:這個屬性是其中子視圖的位置擺布方式默認是填充擺布

  • Fill:子視圖填充他所在的位置(默認)

  • Leading:子視圖頭部對齊

  • Center:子視圖居中對齊

  • Trailing:子視圖尾部對齊

  • Distribution:子視圖的大小

  • Fill:子視圖填充整個UIStackView

  • Fill Equally:子視圖填充空白區域並等分

  • Fill Proportionally:按照目前相對位置進行填充

  • Equal Spacing:等間距

  • Spacing設置子視圖之間的間距大小

  • Baseline Relative:如果設置子視圖間距的大小為基線到下一個視圖的頭部

最後更新:2017-09-05 10:03:35

  上一篇:go  Notion:比插座更好玩的智能家居入門產品
  下一篇:go  從 Yoga 和 Horizon 電腦,看聯想式產品模式創新