ตอนเริ่มสร้างแอปด้วย Xcode คิดว่า Storyboards เป็นเวทมนตร์ Drag-and-drop Subviews วางตำแหน่งง่ายมาก ไม่เข้าใจว่าคนอื่นสร้าง View โดยไม่ใช้ Storyboard ยังไง
สุดท้ายตัดสินใจว่าเพื่อเป็น "Master Programmer" ต้องเรียนรู้การสร้าง View ด้วยโค้ด ปัญหาคือเขียน Constraints ในโค้ดเจ็บปวด
let horizontalConstraint = NSLayoutConstraint( item: newView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0)นี่คือ Horizontal Constraint จัดกึ่งกลาง Subview บนแกน X ของ Superview ไม่ง่ายต่อการอ่าน การเขียนแบบนี้ทุก Constraint ทำให้เบื่อเร็ว แต่อยากเลิก Storyboard จึงหาทางเลือก พบ SnapKit
SnapKit มี API ชัดเจนกระชับ ทำให้เขียน Constraints ในโค้ดง่ายขึ้น มาไปดูตัวอย่างพื้นฐานกัน
จะครอบคลุม:
- จัด Layout Subview ใน Superview
- จัด Layout Subviews เทียบกัน
- Update และ Animate Constraints
เริ่มต้น
ดาวน์โหลด SnapKit ในโปรเจกต์ ผมใช้ CocoaPods สำหรับ Library ภายนอก
เพิ่ม pod 'SnapKit' ใน Podfile รัน pod install เขียน import SnapKit บนไฟล์ที่ต้องการใช้
จัด Layout Subview ใน Superview
ก่อนอื่น Pin Subview ไปขอบ 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) }ตั้ง Constraints ขอบบน ล่าง ซ้าย ขวาของ Subview ให้ตรงขอบ Superview ด้วย Constant 0

สังเกตว่า Add Subview ไป Superview ก่อนตั้ง Constraints ถ้าเขียน Constraints สำหรับ View ที่ยังไม่ Add ไป Superview จะ Error Fatal Runtime ตอน Load View
Syntax SnapKit อ่านง่ายกว่า Standard Library แล้ว แต่ย่อได้อีก SnapKit ให้วิธีสั้นกว่าจัด Constraint View ไปขอบ Superview:
let subview = UIView() view.addSubview(subview)subview.snp.makeConstraints { (make) in make.top.bottom.left.right.equalTo(view) }Layout Subview เหมือนโค้ดข้างต้น แต่ 1 บรรทัดแทน 4 บรรทัด
ยังกำหนดขนาด Subview ได้ ด้านล่างตั้ง Height, Width และจัดกึ่งกลาง Superview:
subview.snp.makeConstraints { (make) in make.width.equalTo(200) make.height.equalTo(200) make.centerX.equalTo(view) make.centerY.equalTo(view) }
ตัวอย่างง่ายแต่ซ้ำโค้ดมาก SnapKit ให้ Chain Constraints ที่มีค่าเดียวกันได้:
subview.snp.makeConstraints { (make) in make.width.height.equalTo(200) make.centerX.centerY.equalTo(view) }Block นี้ให้ Constraints เหมือนข้างต้น อีกวิธี SnapKit ช่วยเขียนโค้ดสะอาด
จัด Layout Subviews เทียบกัน
เพิ่ม Subviews หลายตัวใน View เดียว น่าจะอยาก Layout Subviews เทียบกัน ตัวอย่างนี้:
- เพิ่ม
subview1Constraint ไปขอบซ้าย Superview - เพิ่ม
subview2Constraint ไปขอบขวาของsubview1
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) }จนตอนนี้ Constraints ต้องการ Equal กับ Constraint ตรงข้ามของ Relative View เช่น make.left.equalTo(view) SnapKit ตั้ง left ของ subview1 ให้ left ของ view SnapKit จับคู่ Constraint อัตโนมัติหากไม่ระบุอื่น
สำหรับ Constraint left ของ subview2 ต้องการตั้งให้ขอบขวาของ subview1 ถ้าเขียน make.left.equalTo(subview1) SnapKit จะตั้ง left ของ subview2 ให้ left ของ subview1 แทน จึงใช้ขอบขวาของ subview1 โดยเขียน subview1.snp.right
เข้าถึง Constraint ใดๆ ของ View Layout ได้โดยเพิ่ม snp ตามตัวอย่าง

ตอนนี้ subview2 อยู่ขวา subview1 สร้าง Space ระหว่างกันด้วย Offset เพิ่มใน subview2
subview2.snp.makeConstraints { (make) in make.width.height.equalTo(subview1) make.left.equalTo(subview1.snp.right).offset(50) }Offset เพิ่มท้าย equalTo() ใดๆ เพื่อเปลี่ยน Constant ของ Constraint ตอนนี้ subview2 มี Constant 50 บน Constraint left

Update และ Animate Constraints
อยากให้แอปเป็น #NextBigThing ต้องเพิ่ม Flair นิดหน่อย SnapKit ให้ Update Constraints สร้าง Dynamic Views ได้ง่าย
Update Constraint ด้วย SnapKit คล้ายเพิ่มใหม่ ตัวอย่าง Update Constraint subview1 จากตัวอย่างก่อน เปลี่ยน Constant left จาก 0 เป็น 50:
subview1.snp.updateConstraints { (make) in make.left.equalTo(50) }แค่นี้! Subview จะ Update ไป Constraints ใหม่ตอนรัน Block

สังเกตว่า subview2 เคลื่อนที่ตอน Update subview1 แม้ไม่ Update Constraints ของมัน subview2 Constraint ไปขวา subview1 ตอน subview1 ย้าย subview2 ยังเคารพ Constraint left กับ subview1
เพิ่ม Animation ทำ Transition นุ่มนวล Syntax คุ้นเคยถ้าเคย Animate View:
UIView.animate(withDuration: 0.3) { subview1.snp.updateConstraints { (make) in make.left.equalTo(50) } self.view.layoutIfNeeded() }ตอน Animate Update Constraints ต้องเรียก layoutIfNeeded() บน Parent ของ Subview ถ้า Animate หลาย View พร้อมกัน เรียก layoutIfNeeded() บน Closest Common Superview

updateConstraints() Update Constraints ที่มีอยู่แล้วพยายาม Update Constraint ที่ยังไม่มี จะ Error Fatal Runtime
อยากเพิ่ม Constraints ใหม่ให้ Subview ใช้ remakeConstraints() remakeConstraints() ลบ Constraints เดิมทั้งหมดบน Subview นั้น และเพิ่มใหม่ได้
ยังใช้ deactivate() ลบ Constraint ได้ ด้านล่างสร้าง ตั้ง และลบ Constraint ด้วย deactivate():
var constraint: Constraint! let subview = UIView()subview.snp.makeConstraints { (make) in constraint = make.height.equalTo(100).constraint }constraint.deactivate()ยัง Animate การลบ Constraint ได้โดย Deactivate ภายใน Animation Block
ตอนนี้คุณ Master เขียน Constraints แบบ Programmatic แล้ว Storyboard ดูเหมือนขำขัน ไม่มีเหตุผลกลับไปใช้อีกแล้วใช่ไหม?

ไม่จริง Storyboards และ Nibs ยังเป็นวิธีเร็วที่สุดรวม View Controller และถ้าทำงานกับ Non-Developers เป็นวิธีดีที่สุดแสดงรูปลักษณ์แอปโดยไม่ส่ง Beta
ส่วนตัวผมใช้ทั้งคู่ โปรเจกต์ล่าสุดเพิ่ม Constraints ทั้งโค้ดกับ SnapKit และ Auto Layout บน Storyboards/Nibs บางครั้งบน View เดียวกัน ท้ายที่สุดต้องตัดสินใจว่าอะไรเหมาะกับ View ที่สร้าง
ขอบคุณที่อ่าน! ถ้าชอบ ติดตาม Twitter ผมโพสต์บทความ Product Management, Engineering, และ Design