美文网首页iOS开发 Objective-C
iOS页面的布局方式

iOS页面的布局方式

作者: o慢慢o | 来源:发表于2017-09-30 21:34 被阅读0次

    iOS有三种基本的界面布局的方法,分别是手写UI,xib和storyboard。手写UI是最早进行UI界面布局的方法,优点是灵活自由,缺点是需要写大段的代码进行布局。xib也是比较早出现的UI布局的方式,优点是不需要手写代码,但是每个界面对应一个xib,管理起来复杂。而storyboard则是在iOS5以后出现的,是苹果官方主推的一个代替xib的策略,不仅能将xib汇总统一管理,还可以描述各种场景之间的过渡,缺点是多人协作开发时容易产生冲突。

    下面主要介绍的是手写页面布局。

    一、AutoresizingMasks

    可以使用 AutoresizingMasks 进行页面布局,在 UIView 中有一个autoresizingMask的属性,它对应的是一个枚举的值,属性的意思就是自动调整子控件与父控件中间的位置,宽高。默认值是UIViewAutoresizingNone,控件不会随父视图的改变而改变。

    typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
         UIViewAutoresizingNone = 0,
         UIViewAutoresizingFlexibleLeftMargin = 1 << 0, // 自动调整view与父视图左边距,以保证右边距不变
         UIViewAutoresizingFlexibleWidth = 1 << 1, // 自动调整view的宽度,保证左边距和右边距不变
         UIViewAutoresizingFlexibleRightMargin = 1 << 2, // 自动调整view与父视图右边距,以保证左边距不变
         UIViewAutoresizingFlexibleTopMargin = 1 << 3, // 自动调整view与父视图上边距,以保证下边距不变
         UIViewAutoresizingFlexibleHeight = 1 << 4, // 自动调整view的高度,以保证上边距和下边距不变
         UIViewAutoresizingFlexibleBottomMargin = 1 << 5 // 自动调整view与父视图的下边距,以保证上边距不变
    }
    

    AutoresizingMasks是对未来变化的一种预期,系统会生成frame的布局,当遇到需要使用到多个值的场景时,支持使用|操作符。
    例如,需要设置播放器浮层随播放器大小变化:

    UIView *overlay = [[UIView alloc] init];
    overlay.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    [self addSubview:overlay];
    
    

    Autoresizing需要注意的是,storyboard中设置的约束和手写代码中设置的约束是相反的。storyboard 图形页面里点的右边的线和下边的线的意思是“固定”。

    二、Frame

    frame指的是当前视图在其父视图中的位置和大小。
    在初始化 view 的时候,可以设置 view 的frame

    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(10, 20, 30, 40)]; 
    

    初始化一个距离父视图左边距10,上边距20,宽30,高40的视图。也可以修改声明View的位置以及大小。

    view.frame = CGRectMake(20, 10, 40, 30);
    

    设置和修改视图的 frame 可以完成对界面的布局。

    2.1 bounds

    提到 frame 不得不提 bounds,bounds指的是前视图在其自身坐标系统中的位置和大小。可以看到两者的区别在于坐标系不同。

    2.2 layoutSubviews

    需要重新布局视图可以使用 layoutSubviews

    1)可以在view里重写layoutSubviews

    2)可以在view controller里使用

    viewWillLayoutSubviews 在autoresizingMasks前调用

    viewDidLayoutSubviews 在autoresizingMasks后调用,肯定会覆盖autoresizingMasks的结果

    layoutSubviews可能会在不需要调用的时候调用,如果layoutSubviews的比较复杂,可能会卡顿

    三、自动布局AutoLayout

    前面讲到的 frame 主要用于视图的绝对位置,但是 iOS 设备有多个尺寸,如何对不同尺寸进行适应,苹果的解决方案是使用 AutoLayout。

    如果是从代码层面开始使用 Autolayout,需要对使用的 View 的translatesAutoresizingMaskIntoConstraints 的属性设置为NO。即可开始通过代码添加Constraint,否则View还是会按照以往的autoresizingMask进行计算。而在 Interface Builder 中勾选了Use Auto layout,translatesAutoresizingMaskIntoConstraints 属性都会被默认设置NO。

    3.1 约束

    自动布局里最重要的组成部分就是约束。分别可以设置视图相对于另一个视图的 leading、trailing、top、bottom、CenterX、CenterY 等关系。根据这些约束来确定视图的相对位置。

    视图的约束之间的关系为线型关系。例如,视图Y 相对于 视图X 的位置可以表示为一个线性变换,即

    Y = kX + b

    即 Y 是 X 某个方向坐标或大小的 k 倍并偏移 b。k 和 b 的大小可以是0。如果 k = 1, b = 0, 则表示 Y 和 X 分别表示视图的宽,则等式表示 Y 和 X 的宽度相等。

    AutoLayout 的核心是:Every view requires at least two constraints along each axis to set position and size. 即在每个坐标轴上至少需要2个约束来确定视图位置。

    3.2 Ambiguous Layout

    在开发过程中,你可以通过调用hasAmbiguousLayout 来测试你的view约束是否足够的。函数会返回布尔值。如果有一个不同的frame就会返回yes,如果view的约束完全指定了就会返回no。
    一个设定了完全约束的view的子view也可能存在ambiguous layout,需要为每一个view单独测试layout是否存在ambiguous layout。

    3.3 Intrinsic Content Size

    使用autolayout时,view的content扮演着非常重要的角色。每个view的intrinsicContentSize描述了不会剪切的显示完整view content的最小空间。例如一个image view,content size根据image显示的size设置。一个大的image需要一个大的固有的content size。image的大小提供给了view。
    对于button,固有的content size根据他的title而有不同。随着title增长或者缩短,button的固有的content size也会调节来做适应,可以根据你自定义的font size和title text而有变化。

    3.4 Compression Resistance and Content Hugging

    3.4.1 compression resistance

    压缩阻力表示一个视图的抗压缩性。一个有高compression resistance的视图会防止被压缩。也不会允许content被裁剪,而会尝试保存他的最小固有content size。
    autolayout经常遇到两个冲突的请求。当只有一个请求会成功时,他就会满足高优先级的那个。可以分别设置水平和垂直方向的Compression Resistance。value从1(最低)到1,000(请求的优先级)不等。默认的是750。

    [button setContentCompressionResistancePriority:500 forAxis:UILayoutConstraintAxisHorizontal]; 
    

    3.4.2 content hugging

    抗拉属性表示view防止被拉伸的属性,和压缩阻力类似。默认值为250。

    [button setContentHuggingPriority:501 forAxis:UILayoutConstraintAxisHorizontal];
    

    3.5 VFL

    Visual Format Language,即“可视化格式语言”。直接手写约束很复杂,使用VFL相对简单很多,但比较难进行调试。

      [self.view addConstraints: [NSLayoutConstraint            
    constraintsWithVisualFormat:@"V:[view1]-8-[view2]"             
                        options:NSLayoutFormatAlignAllLeading
                        metrics:nil
                          views:NSDictionaryOfVariableBindings(view1, view2)]];
    

    3.6 Masonry

    VFL的写法也相当复杂,可以使用第三方框架 Masonry,Masonry 是一个轻量级的布局框架,Masonry 源码:https://github.com/Masonry/Masonry

    例如,设置view1相对父View的每个边距离为padding:

    [view1 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(superview).with.insets(padding);
    }];
    

    需要注意的是,在结构一样的情况下用mas_updateConstraints,会更新当前的约束,但是如果要覆盖缘由约束重新添加,则需要使用方法用mas_remakeConstraints

    constraint加到两个view的公共父view上,因此有一个奇怪的现象是一个view不持有自己的约束,而被其他view持有。在 Masonry 中,实际添加constraint的不一定是约束的持有者

    四、更新布局方法

    设置好约束以后,布局是如何更新的呢?

    Constraints

    - (void)updateConstraintsIfNeeded    // 立即重新计算约束,如果在这之前addConstraints,就可以更新约束
    - (void)setNeedsUpdateConstraints   // 立即返回,标记说需要改变约束值,在当前update cycle结束后更新之前所有标记过要改变的约束,调用updateConstraints方法
    

    Layout

    - (void)layoutIfNeeded     // 立即更新布局,重新计算约束,如果在这之前addConstraints就会立即反应在页面上
    - (void)setNeedsLayout    // 同Constraints,不过是更新布局
    - (void)layoutSubviews    // 布局当前页面的子页面
    

    Draw

    - (void)setNeedsDisplay   // 同Constraints,不过是重新渲染
    

    4.1 Constraints,Layout,Draw调用顺序

    一个页面更新的顺序一般为
    调用约束计算出frame(Constraints)→ 根据计算出的frame重新布局(Layout) → 根据重新布局的结果进行图像渲染(Draw)

    layout的改变会导致重新计算Constraints
    layoutIfNeeded会调用updateConstraintsIfNeeded

    Constraints的计算顺序是低到上 (从subview到superview)
    layout的更新顺序是从顶到下(从superview到subview)

    4.2 frame和约束区别

    1、frame是不可以累加的,只能被替换掉,但是constraint可以累加

    2、frame不可以跨级添加,但是contraint可以跨层级

    3、contraint可以设定priority

    另外,需要注意的是,在autolayout下使用frame,会把frame转化成autolayout的约束,如果再进行约束的设置,由于多次累加可能会造成冲突

    github博客:https://wf96390.github.io/blog/2016/03/16/autolayout/

    相关文章

      网友评论

        本文标题:iOS页面的布局方式

        本文链接:https://www.haomeiwen.com/subject/lzbnextx.html