frame 布局

image.png

简单理解 frame 布局,就是制定一个 UI 元素的左上角 x坐标、y坐标、宽度、长度。

这样就能绝对定位一个元素了。

这种布局有个问题是,在不同大小的屏幕上,无法自动调整大小和位置。

例如在 iPhone14 设备上的一块区域,使用 frame 布局,此时方块占了屏幕的大部分宽度,大致位于中间位置。

image.png

显示到 iPad 大屏幕设备上时,大小和位置会发生变化

image.png

autoresize

image.png

UIView有一个 autoresizing 功能,如上图。

四周的短线分别代表 UI 距离父容器的边界距离是否自动调整。点亮时,代表对应方向上的边距固定;不点亮时,代表边距随父容器尺寸变化自动调整。

中间的箭头代表元素自身尺寸是否随父容器变化自动调整,点亮时,代表对应方向上的尺寸进行自动调整。

autolayout 布局

autoresizing 一定程度上可以自动适配UI尺寸,但是仍然有一定局限性,比如只能设置父子容器之间的布局,不能设置兄弟容器之间的布局,而且布局属性也不够丰富。

autolayout 理解

autolayout简单的理解,就一个核心:约束。 可以参考官方文档 Anatomy of a Constraint

image.png

这张图很清晰的描述了一个约束的所有元素组成,含义是 red view 的横向起始是相对于 blue view 的横向结束的 1.0 倍,加上 8 个像素。

其中,leading 是 autolayout 支持的一个属性,下面这张图比较清晰的说明了各个属性的含义:

image.png

错误的 autolayout 布局

image.png
如果只给元素添加一个约束,可以看到会有报错。

约束需要让应用知道如何定位一个UI元素,能够直接或间接的计算出UI元素的左上角 xy坐标、宽度、高度,约束元素 无缺失无冗余歧义

错误的类型见官方文档 布局的错误类型

例如只指定了 leading,没有指定 tail 或者 width,那么应用就无法计算出宽度;如果只指定了 width 和 height,应用无法计算出位置;如果同时指定了 leading、tail、width,则可能产生宽度上的歧义。

有的 UI 元素实现了 intrinsicContentSize 方法,可以根据内容 自动计算视图尺寸,所以自动布局约束不足以计算出准确尺寸时,也不会报错。常见的UI元素有:

  • UILabel:可以根据文本内容自动调整宽度和高度。
  • UITextView:可以根据文本内容自动调整高度。
  • UIButton:可以根据标题和图片自动调整大小。
  • UIImageView:可以根据图像大小自动调整大小。
  • UISlider:可以根据滑块的值自动调整大小。
  • UISwitch:大小是固定的,但可以根据内容自动调整位置。
  • UIStackView:可以根据其子视图的大小和位置自动调整大小。
  • UICollectionViewFlowLayout:可以根据其布局属性自动调整其子视图的大小和位置。
  • UITableView:可以根据其单元格的大小和数量自动调整大小。

抗压缩和抗拉伸

image.png

这个特性比较好理解,当多个元素同时根据内容计算尺寸时,会考虑到抗拉伸和抗压缩,优先级高的越不容易被拉伸和压缩。

xib文件使用

创建 UIViewController 时自动创建 xib 文件

image.png
在创建 UIViewController 时,可勾选自动创建 xib 文件,通过这种方式,将自动创建和 interface 同名的 xib 文件,并将其关联到类文件。

自行创建 xib 文件并关联到类

先创建类文件,再创建 xib 文件,选中 xib 文件的 file’s owner,将 class 设置为类名。
image.png

在 interface 的 init 函数中,从 xib 文件 load view。

1
-(id) init {
    self = [super init];
    if (self) {
        UIView *view = [[[NSBundle mainBundle] loadNibNamed:@"XXXView" owner:self options:nil] firstObject];
        [self addSubview:view];
        view.frame = self.bounds;
    }
    return self;
}

xib文件中的组件自动创建代码中的属性

image.png
开两个窗口,分别打开 UIViewController 和 xib 文件,选中UI组件,按住 control 键拖动到 controller 中,即可自动创建关联属性,代码中可以直接使用。

image.png

在代码中修改元素的约束属性

简单的视图可以直接在 xib 文件中可视化创建和配置,但还是有比较复杂的场景需要通过代码创建。
通过 oc 设置元素约束属性的示例:

1
NSLayoutConstraint *imageViewHeightConstraint = [imageView.heightAnchor constraintEqualToConstant:imageViewHeight];
imageViewHeightConstraint.active = YES;
imageViewHeightConstraint.constant = imageViewHeight;

NSLayoutConstraint *imageViewWidthConstraint = [imageView.widthAnchor constraintEqualToConstant:imageViewWidth];
imageViewWidthConstraint.active = YES;
imageViewWidthConstraint.constant = imageViewWidth;

也可以通过三方库 Masonry 来管理约束。

1
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
    make.left.equalTo(superview.mas_left).with.offset(padding.left);
    make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
    make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];

部分UI组件的布局特性

ScrollView

image.png

ScrollView 在 AutoLayout 中比较特殊,要创建一个滚动区域,我们必须知道 ScrollView 自身宽/高,以及被滚动的内容区域宽/高,才能正确的创建滚动组件。

所以 ScrollView 组件附带了 Content Layout Guide 和 Frame Layout Guide 两个子组件。其中 Content Layout Guide 代表内容区域的布局组件,Frame Layout Guide 代表 ScrollView 的布局属性。

如果只设置 ScrollView 自身的约束,即使设置了完整的约束,还是会报错,原因是无法确定内容区域的约束。将 Content Layout Guide 的宽高约束设置为和 ScrollView 中内容视图一致就可以了。

StackView

向 StackView 中添加元素,可以让元素自动按轴向方向排列,并支持设置轴向方向元素之间的填充或间隔属性;支持设置垂直于轴向方向的填充和对齐属性。
因此 StackView 配合 ScrollView 使用可以创建动态变化尺寸的滚动区域。

相关资料

ios核心动画高级技巧

☞ 参与评论