如何使用Swift iOS以编程方式创建布局与约束
Swift iOS 纯代码创建布局与约束实操指南
从Storyboard转纯代码布局的核心逻辑和你拖线加约束完全一致,只是把可视化点选的操作换成了代码调用,核心还是Auto Layout的那套规则,下面按实际开发的常用流程讲:
前置必记规则
这两个是刚转代码布局最容易踩的基础坑,记牢能少看80%的约束崩溃/冲突日志:
- 所有需要用Auto Layout控制位置的视图,必须把
translatesAutoresizingMaskIntoConstraints属性设为false。这个属性默认是true,系统会自动把旧版Autoresizing的属性转成约束,和你自己写的约束冲突,直接导致布局乱飘。 - 给视图加约束前,必须先把视图通过
addSubview(_:)加到对应的父视图上,顺序写反会直接触发运行时崩溃。
最推荐的原生写法:NSLayoutAnchor(iOS 9+ 全版本支持)
这个API是苹果专门为了简化代码约束设计的,语法和Storyboard里的约束逻辑一一对应,可读性最好,也是目前工业界原生开发的首选写法。
每个UIView、UILayoutGuide(比如SafeArea)都自带一套Anchor属性,对应你在Storyboard里熟悉的约束参照:
topAnchor/bottomAnchor:上/下边界leadingAnchor/trailingAnchor:左/右边界(适配左右布局的语言,比直接用left/rightAnchor更规范)centerXAnchor/centerYAnchor:X轴/Y轴居中widthAnchor/heightAnchor:宽/高
约束写完后用NSLayoutConstraint.activate(_:)批量激活即可,比单个给约束设isActive = true性能更好。
基础示例:带安全区适配的色块
class DemoViewController: UIViewController { // 视图直接通过闭包初始化,和Storyboard拖IBOutlet的作用一致 private let redBlock: UIView = { let view = UIView() view.backgroundColor = .systemRed view.layer.cornerRadius = 8 // 初始化时就关闭自动约束转换,不容易漏 view.translatesAutoresizingMaskIntoConstraints = false return view }() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white // 第一步:先加视图到层级 view.addSubview(redBlock) // 第二步:激活约束 NSLayoutConstraint.activate([ // 顶部距离安全区20pt redBlock.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20), // 左边距父视图20pt redBlock.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), // 右边距父视图20pt(注意右侧/底部的内边距要写负值,和Storyboard里设常量的逻辑一致) redBlock.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20), // 固定高度120pt redBlock.heightAnchor.constraint(equalToConstant: 120) ]) } }
稍复杂示例:登录表单布局
比如实现「顶部距安全区80pt的输入框,输入框下方20pt是和输入框等宽的登录按钮,按钮水平居中」:
// 先初始化输入框和按钮,记得设translatesAutoresizingMaskIntoConstraints = false private let usernameInput: UITextField = { let tf = UITextField() tf.placeholder = "请输入用户名" tf.borderStyle = .roundedRect tf.translatesAutoresizingMaskIntoConstraints = false return tf }() private let loginButton: UIButton = { let btn = UIButton(type: .system) btn.setTitle("登录", for: .normal) btn.backgroundColor = .systemBlue btn.setTitleColor(.white, for: .normal) btn.layer.cornerRadius = 8 btn.translatesAutoresizingMaskIntoConstraints = false return btn }() // addSubview之后激活约束 NSLayoutConstraint.activate([ usernameInput.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 80), usernameInput.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30), usernameInput.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30), usernameInput.heightAnchor.constraint(equalToConstant: 44), loginButton.topAnchor.constraint(equalTo: usernameInput.bottomAnchor, constant: 20), loginButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), loginButton.widthAnchor.constraint(equalTo: usernameInput.widthAnchor), loginButton.heightAnchor.constraint(equalToConstant: 44) ])
其他你可能碰到的旧写法
不用特意去记,看老代码能认出来就行:
- 直接初始化
NSLayoutConstraint:参数非常冗长,需要手动指定item、属性、关联关系、倍数、偏移,可读性差,现在基本没人写 - VFL可视化格式语言:通过字符串描述横向/纵向布局,没有语法提示,写错字符串很难排查,仅在系统约束冲突日志里会输出VFL格式的描述方便定位问题
常见踩坑汇总
- 约束不全:和Storyboard里的橙色警告一个道理,必须给视图确定X/Y位置、宽高四个维度的约束(或者通过内容自适应比如UILabel的固有内容高度补全),少了约束系统会随机补值,导致布局错乱
- 参照对象选错:上下边距要参照
safeAreaLayoutGuide,不要直接参照view的top/bottom,不然内容会被刘海、Home指示条遮挡 - 循环引用:如果在约束闭包里引用了
self,记得加[weak self]避免内存泄漏 - 注意约束优先级:和Storyboard里的优先级逻辑一致,当约束有冲突时,系统会先打破优先级低的约束
如果觉得原生锚点写起来重复代码多,等你把原生API写熟了,可以试试常用的第三方布局库,本质都是对原生约束的封装,上手成本极低。
内容的提问来源于stack exchange,提问作者ChessMaster
相关产品推荐
相关产品推荐

