iOS开发基础11-点像素、Autoresizing、Auto Layout 与 Masonry
iOS 适配与布局全解析:点像素、Autoresizing、Auto Layout 与 Masonry
适配、布局和分辨率是 iOS 开发的基础概念。理解点与像素的关系、Autoresizing 与 Auto Layout 的演进,以及 Masonry 等高效布局工具,是构建多设备兼容应用的关键。
一、适配
1. 什么是适配
适配是让应用在不同环境下都能正常运行并保持良好体验的过程。
2. 移动开发中的适配种类
| 适配类型 | 说明 | 常见手段 |
|---|---|---|
| 系统适配 | 兼容不同 iOS 版本 | 版本判断(@available)、新 API 降级方案、废弃 API 替换 |
| 屏幕适配 | 兼容不同屏幕尺寸和比例 | Auto Layout、Size Classes、Safe Area、动态字体 |
二、点和像素
1. 像素(Pixel)
屏幕的物理发光单元,像素越多屏幕越清晰。用户眼中的屏幕由像素组成。
2. 点(Point)
开发者使用的逻辑坐标单位,与物理像素解耦。一个点由一个或多个像素渲染。
| 设备类型 | 缩放因子 | 1 点 = 多少像素 | 典型设备 |
|---|---|---|---|
| @1x | 1.0 | 1 × 1 | 早期 iPhone 3GS 及以前 |
| @2x | 2.0 | 2 × 2 | iPhone 6/7/8、iPhone X/XS/11 Pro |
| @3x | 3.0 | 3 × 3 | iPhone 6 Plus/7 Plus/8 Plus、iPhone XS Max/11 Pro Max/12+ |
3. 相关 API
Objective-C
// 屏幕逻辑尺寸(点)
CGRect bounds = [UIScreen mainScreen].bounds;
// 屏幕物理像素尺寸
CGRect nativeBounds = [UIScreen mainScreen].nativeBounds;
// 缩放因子(1.0 / 2.0 / 3.0)
CGFloat scale = [UIScreen mainScreen].scale;
Swift
let bounds = UIScreen.main.bounds // 逻辑尺寸(点)
let nativeBounds = UIScreen.main.nativeBounds // 物理像素尺寸
let scale = UIScreen.main.scale // 缩放因子
开发中所有 frame、center、Auto Layout 约束都使用点作为单位,系统自动转换为像素渲染。图片资源通过 @2x/@3x 后缀提供不同分辨率,系统根据设备自动选择。
三、Autoresizing
Autoresizing 是 Auto Layout 之前的布局方案,通过 autoresizingMask 位掩码控制子视图在父视图尺寸变化时的行为。
1. 核心概念
autoresizingMask 是一个位掩码,可组合以下值:
| 枚举值 | 对应 Xib 中的线 | 作用 |
|---|---|---|
UIViewAutoresizingFlexibleLeftMargin |
左边外侧线 | 到父视图左边的距离可变 |
UIViewAutoresizingFlexibleWidth |
中间水平线 | 宽度可变 |
UIViewAutoresizingFlexibleRightMargin |
右边外侧线 | 到父视图右边的距离可变 |
UIViewAutoresizingFlexibleTopMargin |
上边外侧线 | 到父视图上边的距离可变 |
UIViewAutoresizingFlexibleHeight |
中间垂直线 | 高度可变 |
UIViewAutoresizingFlexibleBottomMargin |
下边外侧线 | 到父视图下边的距离可变 |
Xib 中六根线的含义:四周的线勾选表示到对应边的距离固定(不勾选则距离可变,即对应 Flexible Margin);中间的线勾选表示宽/高可变(Flexible Width/Height)。
2. 示例代码
Objective-C
// 父视图
UIView *greenView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
greenView.backgroundColor = [UIColor greenColor];
[self.view addSubview:greenView];
// 子视图
UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
redView.backgroundColor = [UIColor redColor];
[greenView addSubview:redView];
// 宽度和高度随父视图变化
redView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
Swift
let greenView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
greenView.backgroundColor = .green
view.addSubview(greenView)
let redView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
redView.backgroundColor = .red
greenView.addSubview(redView)
redView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
3. 注意事项
- Flexible Width/Height 不是等比拉伸:当父视图尺寸变化时,子视图的宽/高变化量等于父视图的变化量(因为其他边固定,子视图必须吸收全部差值),而非按比例缩放。
- 只能约束父子关系:Autoresizing 无法描述兄弟视图之间的关系(如 A 视图在 B 视图右侧 10pt),这是其最大局限。
- 现代开发:Autoresizing 仍可用于简单场景,但复杂布局应使用 Auto Layout。
四、Auto Layout
Auto Layout 是 iOS 6 引入的自动布局技术,通过约束(Constraint)描述视图间的关系,系统自动计算每个视图的 frame。
1. 约束公式
Auto Layout 的核心是一个线性方程:
view1.attribute1 = (view2.attribute2 × multiplier) + constant
例如:"红色视图的宽度等于父视图宽度的一半减 20":
redView.width = (superview.width × 0.5) + (-20)
2. Storyboard 中的约束状态
| 指示器 | 含义 | 处理方式 |
|---|---|---|
| 红色箭头 | 约束错误(冲突或缺失) | 必须修复,否则布局不确定 |
| 黄色箭头 | 警告(当前帧与约束计算结果不匹配) | 点击 Update Frames 同步,或忽略 |
3. Auto Layout 动画
修改约束后,在动画 block 中调用 layoutIfNeeded 触发布局更新:
Objective-C
// 1. 修改约束的 constant
self.topConstraint.constant = 100;
// 2. 在动画 block 中强制布局
[UIView animateWithDuration:0.3 animations:^{
[self.view layoutIfNeeded]; // 对被修改约束的视图的父视图调用
}];
Swift
topConstraint.constant = 100
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
layoutIfNeeded应发送给被修改约束所在视图的父视图(或更上层),因为约束的布局计算是从父视图向下传递的。
4. 代码实现 Auto Layout
方式一:NSLayoutConstraint 原始 API
Objective-C
UIView *redView = [[UIView alloc] init];
redView.translatesAutoresizingMaskIntoConstraints = NO; // 必须设置,否则 Autoresizing 会转为冲突约束
[self.view addSubview:redView];
// 宽度约束(不依赖其他视图,toItem 传 nil)
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:redView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:100];
// 高度约束
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:redView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:100];
// 居中约束(依赖父视图)
NSLayoutConstraint *centerX = [NSLayoutConstraint constraintWithItem:redView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
NSLayoutConstraint *centerY = [NSLayoutConstraint constraintWithItem:redView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0];
// 激活约束(现代方式,系统自动添加到正确的视图)
[NSLayoutConstraint activateConstraints:@[width, height, centerX, centerY]];
Swift(推荐使用 Anchor API)
let redView = UIView()
redView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(redView)
NSLayoutConstraint.activate([
redView.widthAnchor.constraint(equalToConstant: 100),
redView.heightAnchor.constraint(equalToConstant: 100),
redView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
redView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
iOS 9+ 引入的
NSLayoutAnchor(widthAnchor、centerXAnchor等)提供了类型安全、链式的约束创建方式,是 Swift 中的首选。
约束添加规则
- 约束应添加到能看到所有涉及视图的最近共同祖先视图上。
- 现代 API(
isActive = YES/NSLayoutConstraint.activateConstraints:)会自动处理添加位置,开发者无需手动管理。 translatesAutoresizingMaskIntoConstraints必须设为NO,否则系统会将 Autoresizing 掩码自动转换为约束,与手动添加的约束冲突。
五、VFL(Visual Format Language)
VFL 是一种用字符串描述布局的可视化格式语言,旨在简化 Auto Layout 代码。
1. 语法说明
| 符号 | 含义 |
|---|---|
H: / V: |
水平方向 / 垂直方向 |
| ` | ` |
[view] |
视图 |
[view(==50)] |
视图尺寸(宽度或高度) |
- |
标准间距(8pt) |
-12- |
自定义间距(12pt) |
>= / <= |
大于等于 / 小于等于 |
2. 示例
H:|-[cancelButton(72)]-12-[acceptButton(50)]-|
表示:cancelButton 距父视图左边标准间距,宽度 72;与 acceptButton 间距 12;acceptButton 宽度 50,距父视图右边标准间距。
3. 代码实现
Objective-C
UIButton *cancelButton = [UIButton buttonWithType:UIButtonTypeSystem];
cancelButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:cancelButton];
UIButton *acceptButton = [UIButton buttonWithType:UIButtonTypeSystem];
acceptButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:acceptButton];
NSDictionary *views = NSDictionaryOfVariableBindings(cancelButton, acceptButton);
NSArray *hConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[cancelButton(72)]-12-[acceptButton(50)]-|"
options:0
metrics:nil
views:views];
NSArray *vConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-100-[cancelButton(44)]"
options:0
metrics:nil
views:views];
[NSLayoutConstraint activateConstraints:hConstraints];
[NSLayoutConstraint activateConstraints:vConstraints];
Swift
let cancelButton = UIButton(type: .system)
cancelButton.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(cancelButton)
let acceptButton = UIButton(type: .system)
acceptButton.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(acceptButton)
let views = ["cancelButton": cancelButton, "acceptButton": acceptButton]
let hConstraints = NSLayoutConstraint.constraints(
withVisualFormat: "H:|-[cancelButton(72)]-12-[acceptButton(50)]-|",
options: [], metrics: nil, views: views)
let vConstraints = NSLayoutConstraint.constraints(
withVisualFormat: "V:|-100-[cancelButton(44)]",
options: [], metrics: nil, views: views)
NSLayoutConstraint.activate(hConstraints)
NSLayoutConstraint.activate(vConstraints)
VFL 语法简洁但错误信息不友好、无法表达某些复杂约束(如宽高比、对齐全等),现代开发中已基本被 Masonry/SnapKit 等库取代。
六、Masonry
Masonry 是 OC 中最流行的 Auto Layout 第三方库,通过链式语法大幅简化约束代码。Swift 对应的库是 SnapKit。
1. 基础用法
Objective-C(Masonry)
#import "Masonry.h"
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
[self.view addSubview:button];
[button mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(50);
make.height.mas_equalTo(50);
make.center.equalTo(self.view);
}];
Swift(SnapKit)
import SnapKit
let button = UIButton(type: .system)
view.addSubview(button)
button.snp.makeConstraints { make in
make.width.height.equalTo(50)
make.center.equalToSuperview()
}
2. 常用方法
| 方法 | 作用 |
|---|---|
mas_makeConstraints: |
添加新约束 |
mas_updateConstraints: |
更新已有约束(按属性匹配) |
mas_remakeConstraints: |
移除所有旧约束,重新添加 |
3. mas_equalTo 与 equalTo
| 方法 | 参数类型 | 说明 |
|---|---|---|
mas_equalTo |
基本数据类型 / 结构体 | 自动包装为 NSValue,支持 CGFloat、CGSize、UIEdgeInsets 等 |
equalTo |
id 对象 |
需传入对象类型,如 UIView、NSNumber |
添加宏 #define MAS_SHORTHAND_GLOBALS 后,equalTo 也支持基本类型,两者无差别。
4. 语义化方法:with 和 and
make.top.left.and.right.equalTo(self.view);
make.height.mas_equalTo(44);
with 和 and 仅返回 self,不改变逻辑,用于提高代码可读性。
5. Safe Area 适配(iOS 11+)
iOS 11 引入 Safe Area,避免内容被刘海、底部横条遮挡:
Objective-C
[button mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop);
make.left.right.equalTo(self.view);
make.height.mas_equalTo(44);
}];
Swift(SnapKit)
button.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide.snp.top)
make.left.right.equalToSuperview()
make.height.equalTo(44)
}
七、总结
| 布局方案 | 引入版本 | 能力 | 适用场景 |
|---|---|---|---|
| Autoresizing | iOS 1 | 仅父子关系,简单伸缩 | 简单布局、临时视图 |
| Auto Layout(原生) | iOS 6 | 任意视图间关系,功能强大 | 所有布局场景 |
| VFL | iOS 6 | 字符串描述水平/垂直布局 | 已基本淘汰 |
| Masonry / SnapKit | 第三方 | 链式语法,封装 Auto Layout | 代码布局首选 |
| SwiftUI | iOS 13 | 声明式布局 | 新项目、跨平台 |
- 点与像素的解耦让开发者无需关心具体设备分辨率,通过 @2x/@3x 资源提供清晰画面。
- Auto Layout 是现代 iOS 布局的基础,理解约束公式和
layoutIfNeeded动画机制是排查布局问题的关键。 - 代码布局优先使用 Masonry(OC)或 SnapKit(Swift),避免冗长的原生 API。
- iOS 11+ 必须考虑 Safe Area,避免内容被刘海和底部横条遮挡。

浙公网安备 33010602011771号