Quantcast
Viewing all articles
Browse latest Browse all 5930

iOS AutoLayout全解

AutoLayout简介

Autolayout是一种全新的布局技术,专门用来布局UI界面的,用来取代Frame布局在遇见屏幕尺寸多重多样的问题。Autolayout自iOS 6开始引入,但是由于Xcode 4的不给力,当时并没有得到大规模推广。在iOS 7(Xcode5)开始,Autolayout的开发效率得到很大的提升,苹果官方也推荐开发者尽量使用Autolayout来布局UI界面,减少纯代码的方式。
那么AutoLayout怎么使用呢?
Image may be NSFW.
Clik here to view.
这里写图片描述

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就能够按照约束来局。
Image may be NSFW.
Clik here to view.
这里写图片描述

Image may be NSFW.
Clik here to view.
这里写图片描述

添加如下约束:
Image may be NSFW.
Clik here to view.
这里写图片描述

属性说明:
1:距离边缘 最上面的4个虚线表示某个View的距离上边 左边 右边 下边多高
2:那个蓝色的Constrain To Margins 是iPhone6出现之后。
Apple 觉得更大的分辨率有点间距好看, 默认为8 , 如果这个勾上了 这个View距离四周的值就变成了 你输入的值+8。 一般建议勾掉 。

案例1

设置某个View距离父View上下左右间距全部为20。
Image may be NSFW.
Clik here to view.
这里写图片描述

案例2

某个View距离父View的左侧20,上20,宽高均为100。
Image may be NSFW.
Clik here to view.
这里写图片描述

注意:我在添加约束的时候有个选项叫做updateFrame 如果勾选 会直接将Frame调整到真实值 ,而不需要再次update 。

案例3

某个View距离在父View的左侧20 案例2中白色View 上20 宽高和Demo2中的宽高一样。
Image may be NSFW.
Clik here to view.
这里写图片描述

然后,点击某个约束。
Image may be NSFW.
Clik here to view.
这里写图片描述

对其处理

Image may be NSFW.
Clik here to view.
这里写图片描述

属性说明:

  • 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中的宽高一样 并且对齐。
Image may be NSFW.
Clik here to view.
这里写图片描述

案例5

某个label和另外一个label基线对齐。
Image may be NSFW.
Clik here to view.
这里写图片描述

像label 默认是有宽度的 宽度就是字体自适应的。这样我们就可以不给UIlabel 高度 把Label的NumberOfline = 0就可以自适应高度了。
Image may be NSFW.
Clik here to view.
这里写图片描述

Tip

1,有时候约束太多的时候 我们可以给某个View起个假名字以起到唯一标识的作用。
2,View总是选不中怎么办?按 ctrl + shift + 单击。
3,当ScrollView过长无法编辑怎么办?将控制器改为Freedom 修改ContentView的高度约束 这样ScrollView 就可以滚动了。
Image may be NSFW.
Clik here to view.
这里写图片描述

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即可。
Image may be NSFW.
Clik here to view.
这里写图片描述

还有由于UITableViewCell需要重用功能,所以我们还需要设置一个重用标识。
Image may be NSFW.
Clik here to view.
这里写图片描述

下面是使用autoLayout的布局。
Image may be NSFW.
Clik here to view.
这里写图片描述

接着我们在UITableView中来使用我们自定义的UITableViewCell C1。首先我们创建一个UITableViewController的子类T1ViewController, 接着在Main.storyboard中拖入一个UITableViewController,并关联T1ViewController。
Image may be NSFW.
Clik here to view.
这里写图片描述

Auto Layout with UITextView

同样参考上面我们创建一个C2.xib, UITableViewCell的子类C2,并关联C2.xib与C2类。并在C2.xib中对其布局,同样使用了auto layout. 布局如下图:
Image may be NSFW.
Clik here to view.
这里写图片描述

创始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的实例:
Image may be NSFW.
Clik here to view.
这里写图片描述

此图中距顶的约束是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之类的了,如图:
Image may be NSFW.
Clik here to view.
这里写图片描述

然后添加如下的计算代码:

- (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));
}

运行效果如下:
Image may be NSFW.
Clik here to view.
这里写图片描述

Manual Layout with UITextView

按照前面介绍的,我们需要创建C4.xib, C4类, T4ViewController类,Main.storyboard中拖入UITableViewController,并分别建立关联。 为了简单,C4.xib中我就不加padding之类的了,如图:
Image may be NSFW.
Clik here to view.
这里写图片描述

相关代码如下:

- (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实现视图等分的例子。
Image may be NSFW.
Clik here to view.
这里写图片描述

相关代码如下:

 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。
Image may be NSFW.
Clik here to view.
这里写图片描述

2,然后再添加三个imageView,将界面上的四个ImageView设置等宽等高。
Image may be NSFW.
Clik here to view.
这里写图片描述

3,然后再将他们加入到Stack View中,设置Stack View 的distribution属性为 Equal Spacing(等间距)。
Image may be NSFW.
Clik here to view.
这里写图片描述

Image may be NSFW.
Clik here to view.
这里写图片描述

4,最后,设置Stack View 的位置
Image may be NSFW.
Clik here to view.
这里写图片描述

Image may be NSFW.
Clik here to view.
这里写图片描述

作者:xiangzhihong8 发表于2017/9/4 11:28:43 原文链接
阅读:209 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles