iOS AutoLayout全解

来源:互联网 发布:2017中国大学生数据 编辑:程序博客网 时间:2024/05/17 10:25

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,可以使用下面两个第三方库:OAStackView和TZStackView。其中:

  • 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:如果设置子视图间距的大小为基线到下一个视图的头部

最近在研究AR技术,苹果的ARKit和Google的ARCore技术,有兴趣的读者请关注微信公众号:
这里写图片描述

原创粉丝点击