Khi mới bắt đầu xây dựng app với Xcode, tôi nghĩ Storyboard là phép màu. Kéo thả subview vào vị trí quá đơn giản, tôi không hiểu người ta xây dựng view không dùng chúng như thế nào.
Cuối cùng, quyết định để trở thành "master programmer", cần học xây dựng view programmatically. Vấn đề, viết constraint trong code rất đau đầu.
let horizontalConstraint = NSLayoutConstraint( item: newView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0)Đó là constraint ngang căn giữa subview trên trục X của superview. Không dễ đọc, viết đó cho mọi constraint nhanh chán. Vẫn muốn bỏ storyboard nên tìm alternative. Đó là lúc tìm thấy SnapKit.
SnapKit có API rõ ràng, ngắn gọn khiến viết constraint trong code dễ dàng. Sẽ đi qua vài ví dụ cơ bản SnapKit có thể làm.
Sẽ bao gồm:
- Layout subview trong superview
- Layout subview liên quan lẫn nhau
- Cập nhật và animation constraint
Bắt Đầu
Đảm bảo tải SnapKit vào project. Tôi dùng CocoaPods cho thư viện thứ 3.
Thêm pod 'SnapKit' vào Podfile chạy pod install. Giờ viết import SnapKit đầu file muốn dùng.
Layout Subview Trong Superview
Trước hết ghim subview vào cạnh superview:
let subview = UIView()
view.addSubview(subview)subview.snp.makeConstraints { (make) in
make.top.equalTo(view)
make.bottom.equalTo(view)
make.left.equalTo(view)
make.right.equalTo(view)
}Đặt constraint top, bottom, left, right của subview tới cạnh tương ứng superview với constant 0.

Lưu ý thêm subview vào superview trước khi đặt constraint. Nếu viết constraint cho view chưa thêm vào superview, sẽ throw fatal runtime error khi view load.
Cú pháp SnapKit đã dễ đọc hơn standard library nhưng có thể ngắn gọn hơn. SnapKit cung cấp cách ngắn gọn hơn ghim view vào cạnh superview:
let subview = UIView()
view.addSubview(subview)subview.snp.makeConstraints { (make) in
make.top.bottom.left.right.equalTo(view)
}Layout subview giống code trên nhưng chỉ 1 dòng thay vì 4.
Cũng constraint size subview. Dưới đây set height, width subview và đặt giữa superview:
subview.snp.makeConstraints { (make) in
make.width.equalTo(200)
make.height.equalTo(200)
make.centerX.equalTo(view)
make.centerY.equalTo(view)
}
Ví dụ đơn giản nhưng lặp lại nhiều. Khi đặt constraint cùng giá trị, SnapKit cho phép chain constraint:
subview.snp.makeConstraints { (make) in
make.width.height.equalTo(200)
make.centerX.centerY.equalTo(view)
}Block này cho constraint giống block trên. Cách nữa SnapKit giúp code sạch hơn.
Layout Subview Liên Quan Lẫn Nhau
Khi thêm nhiều subview vào một view, thường muốn layout subview liên quan nhau. Ví dụ này:
- Thêm
subview1constraint vào cạnh trái superview - Thêm
subview2constraint vào cạnh phảisubview1
let subview1 = UIView()
let subview2 = UIView()
view.addSubview(subview1)
view.addSubview(subview2)subview1.snp.makeConstraints { (make) in
make.width.height.equalTo(100)
make.left.equalTo(view)
}subview2.snp.makeConstraints { (make) in
make.width.height.equalTo(subview1)
make.left.equalTo(subview1.snp.right)
}Cho đến giờ muốn constraint bằng constraint tương ứng của view liên quan. Ví dụ, make.left.equalTo(view) SnapKit set left của subview1 bằng left của view. SnapKit tự match constraint nếu không chỉ định khác.
Cho constraint left của subview2, muốn set bằng cạnh right của subview1. Nếu viết make.left.equalTo(subview1) SnapKit set left của subview2 bằng left của subview1. Thay vào đó, dùng cạnh right của subview1 bằng cách viết subview1.snp.right.
Có thể truy cập bất kỳ constraint nào của view layout bằng thêm snp như trên.

Giờ có subview2 bên phải subview1, tạo khoảng cách bằng thêm offset cho subview2.
subview2.snp.makeConstraints { (make) in
make.width.height.equalTo(subview1)
make.left.equalTo(subview1.snp.right).offset(50)
}Offset append cuối equalTo() thay đổi constant constraint. Giờ subview2 constant 50 trên constraint left.

Cập Nhật Và Animation Constraint
Muốn app thành #NextBigThing cần thêm chút flair! SnapKit cung cấp cách dễ dàng update constraint tạo view động.
Update constraint với SnapKit gần như giống thêm mới. Ở đây, update constraint subview1 từ ví dụ trước bằng cách đổi constant constraint left từ 0 thành 50:
subview1.snp.updateConstraints { (make) in
make.left.equalTo(50)
}Và xong! Subview update constraint mới khi block chạy.

Có thể nhận ra subview2 di chuyển khi update subview1 dù không update constraint nó. subview2 constraint vào cạnh phải subview1. Khi subview1 di chuyển, subview2 tiếp tục tuân thủ constraint left với subview1.
Giờ thêm animation làm mượt transition. Nếu từng animate view, cú pháp quen thuộc:
UIView.animate(withDuration: 0.3) {
subview1.snp.updateConstraints { (make) in
make.left.equalTo(50)
}
self.view.layoutIfNeeded()
}Khi animate update constraint, cần gọi layoutIfNeeded() trên parent của subview. Nếu animate nhiều view cùng lúc, gọi layoutIfNeeded() trên closest common superview của subview.

updateConstraints() chỉ update constraint hiện có. Cố update constraint chưa có sẽ gây fatal runtime error.
Muốn thêm constraint mới cho subview, dùng remakeConstraints(). remakeConstraints() sẽ xóa constraint hiện có trên subview đó và cho thêm mới.
Cũng dùng deactivate() xóa constraint. Dưới tạo, set, rồi xóa constraint dùng deactivate():
var constraint: Constraint!
let subview = UIView()subview.snp.makeConstraints { (make) in
constraint = make.height.equalTo(100).constraint
}constraint.deactivate()Cũng animate xóa constraint bằng deactivate trong animation block.
Vậy bạn thành thạo viết constraint programmatically. Storyboard giống đùa và không lý do quay lại, đúng không?

Không hẳn. Storyboard và nib vẫn cách nhanh nhất gom view controller, và nếu làm việc với non-developer, là cách tốt nhất show app trông như thế nào mà không gửi beta.
Cá nhân tôi dùng cả hai. App cuối cùng xây, thêm constraint cả code SnapKit và Auto Layout storyboard/nib, đôi khi trên cùng view. Cuối cùng, bạn quyết định cách nào tốt cho view bạn xây.
Cảm ơn đã đọc! Nếu thích, follow Twitter tôi đăng bài về product management, engineering, design.