Interface Builder — โปรแกรมแก้ไขอินเทอร์เฟซแบบภาพที่รวมอยู่ใน Xcode สำหรับการพัฒนา iOS และ macOS ช่วยให้สร้าง UI ผ่าน drag-and-drop กำหนดค่า Auto Layout เชื่อมต่อโค้ดผ่าน IBOutlet และ IBAction มาวิเคราะห์กันว่า IB ทำงานอย่างไร, Storyboard และ XIB ต่างกันอย่างไร, และ @IBDesignable มีไว้เพื่ออะไร
ประเด็นสำคัญ
Interface Builder — ส่วนประกอบของ Xcode ที่ออกแบบมาสำหรับการออกแบบอินเทอร์เฟซผู้ใช้แบบภาพ ประวัติของ IB เริ่มต้นในปี 1988 ที่บริษัท NeXT ก่อนการมาถึงของ iOS Stefan Pope พัฒนาเวอร์ชันแรกสำหรับ NeXTSTEP — ระบบปฏิบัติการที่กลายเป็นพื้นฐานของ macOS และ iOS ในปี 1996 Apple ซื้อ NeXT และรวม Interface Builder เข้าใน Xcode
ใน Xcode สมัยใหม่ Interface Builder รองรับสามรูปแบบไฟล์: Storyboard, XIB (Xcode Interface Builder) และ ไฟล์ XIB สำหรับเซลล์ตาราง และวิวที่กำหนดเอง แต่ละรูปแบบเหล่านี้เก็บคำอธิบาย XML ของลำดับชั้นองค์ประกอบ UI คุณสมบัติ ข้อจำกัด และการเชื่อมต่อกับโค้ด
IB ทำงานในระดับ UIKit: ปุ่ม ป้ายกำกับ ฟิลด์ข้อความ ตาราง คอลเลกชัน และข้อจำกัดถูกลากด้วยเมาส์ไปยังผืนผ้าใบ Xcode คอมไพล์ไฟล์ .storyboard และ .xib เป็น ไฟล์เก็บถาวร nib (compiled Interface Builder) ในขั้นตอน build ซึ่งลดขนาด bundle และเพิ่มความเร็วในการโหลด
ตามข้อมูลของ Apple กว่า 70% ของโปรเจกต์ iOS บน UIKit ใช้ Interface Builder ในขั้นตอนต่างๆ ของการพัฒนา แม้ SwiftUI จะเติบโตขึ้น แต่ IB ยังคงเป็นมาตรฐานสำหรับแอปพลิเคชันเชิงพาณิชย์ที่รองรับ iOS 12 และต่ำกว่า รวมถึงอินเทอร์เฟซที่กำหนดเองที่ซับซ้อนซึ่งต้องการการปรับแต่ง Auto Layout อย่างละเอียด
ก่อน Xcode 4 Interface Builder เป็นแอปพลิเคชันแยกต่างหากที่ทำงานควบคู่กับโปรแกรมแก้ไขโค้ด ใน Xcode 4 (2011) Apple รวม IB และโปรแกรมแก้ไขโค้ดเป็น IDE เดียวกัน ซึ่งทำให้สามารถสลับระหว่างโค้ดและเลย์เอาต์ได้โดยไม่ต้องเปลี่ยนหน้าต่าง รวมถึงเห็นการเปลี่ยนแปลงคุณสมบัติแบบเรียลไทม์ผ่าน แผง Attributes Inspector
| เวอร์ชัน Xcode | ปี | การเปลี่ยนแปลงใน Interface Builder |
|---|---|---|
| Xcode 3 | 2008 | IB — แอปพลิเคชันแยก, รองรับ iOS 2.0 |
| Xcode 4 | 2011 | IB รวมอยู่ใน IDE, Storyboard ปรากฏ |
| Xcode 5 | 2013 | Auto Layout พร้อมเมนูข้อจำกัด, ตัวอย่างหน้าจอ |
| Xcode 6 | 2014 | Size Classes, @IBDesignable, Preview Assistant |
| Xcode 11 | 2019 | SwiftUI Canvas, IB ยังคงสำหรับ UIKit |
| Xcode 15 | 2023 | SwiftUI Preview เป็นเครื่องมือหลัก, IB โหมดเก่า |
ด้วยการมาถึงของ SwiftUI ในปี 2019 Apple ได้เปลี่ยนโฟกัสไปที่การพัฒนาแบบประกาศ อย่างไรก็ตาม Interface Builder ยังคงรวมอยู่ใน Xcode เพื่อรองรับโปรเจกต์ UIKit แอปพลิเคชันที่มีอยู่หลายพันรายการยังคงใช้ IB และ Apple ยังไม่ได้ประกาศการลบออก
Interface Builder รองรับสองรูปแบบหลัก: Storyboard (.storyboard) และ XIB (.xib) ความแตกต่างระหว่างทั้งสองอยู่ในขอบเขตและสถานการณ์การใช้งาน
Storyboard — ไฟล์ที่มีทั้งฉากของแอปพลิเคชัน: หลายหน้าจอ (UIViewController), การเปลี่ยนผ่านระหว่างกัน (segues), ตัวควบคุมการนำทาง, แท็บแถบ และองค์ประกอบ UI ทั้งหมด Storyboard โหลดครั้งเดียวเมื่อเริ่มต้นจาก Info.plist ผ่านคีย์ UIMainStoryboardFile (k) สะดวกสำหรับการแสดงภาพโฟลว์หน้าจอ แต่สร้างปัญหาเมื่อเกิดข้อขัดแย้งในการ merge ใน git เนื่องจากคำอธิบาย XML ของทั้งแอปพลิเคชันถูกเก็บในไฟล์เดียว
XIB (ย่อมาจาก Xcode Interface Builder) — ไฟล์สำหรับหนึ่งองค์ประกอบ: UIView, UITableViewCell, UICollectionViewCell หรือ ViewController หนึ่งตัว XIB โหลดตามต้องการผ่าน UINib(nibName:bundle:) (k) หรือเมธอด Bundle.loadNibNamed (k) ไฟล์ XIB merge ได้ง่ายกว่า กะทัดรัดกว่า และโหลดเร็วกว่าเนื่องจากไม่มีคำอธิบายของทั้งแอปพลิเคชัน
| เกณฑ์ | Storyboard | XIB |
|---|---|---|
| ขอบเขต | หลายหน้าจอ + การเปลี่ยนผ่าน | หนึ่งหน้าจอหรือองค์ประกอบ |
| Segues | รองรับ (push, modal, unwind) | ไม่รองรับ |
| Merge ใน git | ซับซ้อน (XML ใหญ่หนึ่งไฟล์) | ง่าย (หลายไฟล์เล็ก) |
| การโหลด | เมื่อเริ่มแอปพลิเคชัน | ตามต้องการ (ขี้เกียจ) |
| การใช้ซ้ำ | ผ่าน storyboard references เท่านั้น | สูง (เซลล์, ส่วนหัว, วิว) |
| คำแนะนำของ Apple | ไม่แนะนำสำหรับโปรเจกต์ใหญ่ | แนะนำสำหรับคอมโพเนนต์ |
ตั้งแต่เวอร์ชัน Xcode 11 Apple แนะนำให้ใช้ XIB สำหรับคอมโพเนนต์เดี่ยวๆ และหลีกเลี่ยง Storyboard แบบเสาหิน สำหรับการนำทางระหว่างหน้าจอ ควรใช้การนำทางผ่านโค้ดด้วย UIStoryboardSegue (k) ด้วยตนเองหรือผู้ประสานงาน
ไฟล์ .storyboard และ .xib เก็บ XML ในรูปแบบ Interface Builder Cocoa Touch XIB (dt) ตัวอย่างโครงสร้างแบบย่อ:
<!-- ไฟล์ XIB พร้อม UIView และ UILabel -->
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB"
version="3.0">
<objects>
<view id="abc-123"
userLabel="CustomHeaderView"
contentMode="scaleToFill">
<subviews>
<label id="def-456"
text="หัวเรื่อง"
textColor="darkTextColor"
fontDescription="title1"/>
</subviews>
</view>
</objects>
</document>แต่ละองค์ประกอบมี id (an) เฉพาะ ซึ่ง IB ใช้เชื่อมต่อโหนด XML กับออบเจกต์รันไทม์ เมื่อคอมไพล์ Xcode แปลง XML เป็นรูปแบบ nib ไบนารี (.nib) ลดขนาดไฟล์ลงประมาณ 40%
Auto Layout — ระบบวางตำแหน่งองค์ประกอบบนหน้าจอผ่านความสัมพันธ์ทางคณิตศาสตร์ (ข้อจำกัด) Interface Builder มีอินเทอร์เฟซแบบภาพสำหรับสร้าง แก้ไข และดีบักข้อจำกัดโดยไม่ต้องเขียนโค้ด ข้อจำกัดแต่ละข้ออธิบายการพึ่งพา: view.leading = superview.leading + 16 (k) หรือ view.width = 2 * otherView.height (k)
ใน IB ข้อจำกัดถูกสร้างผ่านเมนู Pin (การยึดระยะขอบ, ความกว้าง, ความสูง) และ Align (การจัดแนวตรงกลาง, ขอบ, baseline) แผง Size Inspector แสดงข้อจำกัดทั้งหมดขององค์ประกอบที่เลือก ลำดับความสำคัญ (required/high/low) และให้แก้ไขตัวคูณและค่าคงที่
IB ยังรองรับ UIStackView — คอนเทนเนอร์ที่จัดการตำแหน่งของวิวลูกโดยอัตโนมัติ เพียงวางองค์ประกอบใน stack view บนผืนผ้าใบ IB จะสร้างข้อจำกัดที่จำเป็นโดยอัตโนมัติ ซึ่งช่วยเร่งการจัดวางเมื่อเทียบกับการวางข้อจำกัดด้วยตนเอง
Size Classes — สิ่งที่เป็นนามธรรมที่จัดกลุ่มอุปกรณ์ตามความกว้างและความสูงของหน้าจอ: Compact และ Regular ชุดค่าผสม (wC hR สำหรับ iPhone แนวตั้ง, wR hR สำหรับ iPad) ช่วยให้กำหนดข้อจำกัดและการจัดวางองค์ประกอบที่แตกต่างกันสำหรับสถานการณ์ต่างๆ ใน Interface Builder การสลับระหว่าง size classes จะเปลี่ยนชุดข้อจำกัดที่ทำงานอยู่บนผืนผ้าใบ
| อุปกรณ์ | การวางแนว | Width Class | Height Class |
|---|---|---|---|
| iPhone (ยกเว้น Max/Plus) | แนวตั้ง | Compact | Regular |
| iPhone (ยกเว้น Max/Plus) | แนวนอน | Compact | Compact |
| iPhone Plus/Max | แนวนอน | Regular | Compact |
| iPad | ใดๆ | Regular | Regular |
| iPad Split View | 1/3 หน้าจอ | Compact | Regular |
ตัวอย่างข้อจำกัดที่มีรูปแบบตาม size class:
import UIKit
class AdaptiveViewController: UIViewController {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var leadingConstraint: NSLayoutConstraint!
private func updateConstraints() {
let isRegular = traitCollection.horizontalSizeClass == .regular
leadingConstraint.constant = isRegular ? 40 : 16
titleLabel.font = isRegular
? UIFont.preferredFont(forTextStyle: .largeTitle)
: UIFont.preferredFont(forTextStyle: .title1)
}
override func traitCollectionDidChange(
_ previousTraitCollection: UITraitCollection?
) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.horizontalSizeClass != previousTraitCollection?.horizontalSizeClass {
updateConstraints()
}
}
}ในโค้ดด้านบน traitCollectionDidChange ตอบสนองต่อการเปลี่ยนแปลง size class อัปเดตข้อจำกัดและแบบอักษร Interface Builder ช่วยให้กำหนดค่าเริ่มต้นสำหรับแต่ละ size class ผ่านตัวตรวจสอบ และโค้ดใช้สำหรับสถานการณ์ไดนามิกที่ไม่สามารถอธิบายแบบคงที่ได้
การเชื่อมต่อระหว่างอินเทอร์เฟซแบบภาพใน Interface Builder และโค้ด Swift/Objective-C เกิดขึ้นผ่านสองกลไก: IBOutlet (Interface Builder Outlet) และ IBAction (Interface Builder Action) ทั้งสองสร้างขึ้นโดยการลากด้วยแป้น Ctrl ค้างจากผืนผ้าใบ IB ไปยังไฟล์ตัวควบคุม
IBOutlet — คำอธิบายประกอบที่ประกาศการอ้างอิงถึงองค์ประกอบ UI Xcode จะเชื่อมต่อกับออบเจกต์ที่เกี่ยวข้องในไฟล์เก็บถาวร nib โดยอัตโนมัติเมื่อโหลด หากการเชื่อมต่อขาด (เช่น องค์ประกอบถูกเปลี่ยนชื่อ) แอปพลิเคชันจะหยุดทำงานด้วยข้อผิดพลาด NSUnknownKeyException (k) IBOutlet ถูกทำเครื่องหมายเป็น weak (k) เนื่องจาก nib เป็นเจ้าของออบเจกต์และตัวควบคุมเป็นเพียงผู้สังเกตการณ์
IBAction — เมธอดที่ถูกเรียกเมื่อเกิดเหตุการณ์ขององค์ประกอบ UI: การกดปุ่ม, การเปลี่ยนข้อความ, การสลับสวิตช์ IB เชื่อมต่อ UIControlEvent (k) กับเมธอดผ่าน addTarget:action:forControlEvents: (k) ในโค้ด IBAction ปรากฏเป็นเมธอดปกติที่มีประเภทส่งคืน IBAction (dt)
import UIKit
final class LoginViewController: UIViewController {
@IBOutlet weak var emailTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var loginButton: UIButton!
@IBOutlet weak var spinner: UIActivityIndicatorView!
@IBAction private func loginButtonTapped(_ sender: UIButton) {
guard let email = emailTextField.text, !email.isEmpty,
let password = passwordTextField.text, !password.isEmpty
else {
showAlert(message: "กรอกข้อมูลทุกช่อง")
return
}
loginButton.isEnabled = false
spinner.startAnimating()
performLogin(email: email, password: password)
}
private func performLogin(email: String, password: String) {
/// การเรียก API ผ่าน URLSession
let request = LoginRequest(email: email, password: password)
APIClient.shared.login(request) { [weak self] result in
DispatchQueue.main.async {
guard let self else { return }
self.spinner.stopAnimating()
self.loginButton.isEnabled = true
switch result {
case .success:
self.navigateToMainScreen()
case .failure(let error):
self.showAlert(message: error.localizedDescription)
}
}
}
}
private func showAlert(message: String) {
let alert = UIAlertController(
title: "ข้อผิดพลาด",
message: message,
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}ในตัวอย่างแสดงการเชื่อมต่อมาตรฐาน: IBOutlet สำหรับฟิลด์ข้อความ ปุ่ม และสปินเนอร์, IBAction สำหรับจัดการการกดปุ่ม การเชื่อมต่อทั้งหมดนี้ถูกตั้งค่าใน Interface Builder ผ่าน Ctrl+drag หากไม่ได้กำหนดค่าการเชื่อมต่อ IBOutlet จะเป็น nil (v) เมื่อรันไทม์ ซึ่งจะทำให้ crash เมื่อเข้าถึง — ดังนั้น IBOutlet จึงถูกประกาศเป็น weak var (k s) พร้อม implicit unwrap
@IBDesignable — คำอธิบายประกอบ Swift ที่ช่วยให้แสดง UIView ที่กำหนดเองบนผืนผ้าใบ Interface Builder แบบเรียลไทม์ นักพัฒนาจะเห็นผลลัพธ์ของการเปลี่ยนแปลงโค้ดโดยไม่ต้องเปิดแอปพลิเคชัน @IBInspectable — คำอธิบายประกอบสำหรับคุณสมบัติที่เพิ่มลงในแผง Attributes Inspector ของ IB ซึ่งสามารถเปลี่ยนค่าได้แบบโต้ตอบ
คำอธิบายประกอบเหล่านี้มีประโยชน์โดยเฉพาะเมื่อสร้างไลบรารีคอมโพเนนต์ UI: ปุ่มที่กำหนดเอง, ฟิลด์ป้อนข้อมูลพร้อมหน้ากาก, ตัวบ่งชี้แบบเคลื่อนไหว IBDesignable ใช้ prepareForInterfaceBuilder() (fn) สำหรับการคอมไพล์โค้ด build แยกต่างหากที่ไม่ส่งผลต่อไบนารีหลักของแอปพลิเคชัน
import UIKit
@IBDesignable
final class GradientButton: UIButton {
@IBInspectable var startColor: UIColor = .systemBlue {
didSet { updateGradient() }
}
@IBInspectable var endColor: UIColor = .systemPurple {
didSet { updateGradient() }
}
@IBInspectable var cornerRadius: CGFloat = 12 {
didSet {
layer.cornerRadius = cornerRadius
layer.masksToBounds = true
}
}
private let gradientLayer = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame: frame)
setupGradient()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupGradient()
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = bounds
}
private func setupGradient() {
layer.insertSublayer(gradientLayer, at: 0)
updateGradient()
}
private func updateGradient() {
gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setupGradient()
}
}ในโค้ดด้านบน GradientButton เป็นคอมโพเนนต์ IBDesignable ที่มีคุณสมบัติ IBInspectable startColor (v), endColor (v) และ cornerRadius (v) เมื่อลาก UIView ไปยังผืนผ้าใบ IB และเปลี่ยนคลาสเป็น GradientButton ใน Identity Inspector ปุ่มที่มีการไล่ระดับสีจะปรากฏบนผืนผ้าใบแบบเรียลไทม์ คุณสมบัติ IBInspectable ทั้งหมดจะปรากฏในแผง Attributes Inspector ทางด้านขวา
สำคัญ: @IBDesignable คอมไพล์โค้ดทั้งหมดเพื่อแสดงใน IB ดังนั้นจึงไม่ควรดำเนินการร้องขอเครือข่ายหรือการดำเนินการที่ยาวนานภายในนั้น สำหรับการแยกความแตกต่าง ใช้ #if TARGET_INTERFACE_BUILDER (k) — การคอมไพล์แบบมีเงื่อนไขที่ไม่รวมโค้ดที่ไม่ได้มีไว้สำหรับ IB
กระบวนการแปลงไฟล์ Interface Builder จากการสร้าง nib ไปจนถึงการแสดงบนหน้าจอประกอบด้วยหลายขั้นตอน การทำความเข้าใจวงจรนี้ช่วยวินิจฉัยปัญหากับ IB
ในขั้นตอน build Xcode จะเรียกใช้เครื่องมือ ibtool (k, fn) — ยูทิลิตีบรรทัดคำสั่งสำหรับคอมไพล์ไฟล์ .storyboard และ .xib เป็นรูปแบบ nib ไบนารี ibtool ยังตรวจสอบความถูกต้อง: ตรวจสอบความถูกต้องของข้อจำกัด การมีอยู่ของคลาสทั้งหมด ประเภทการเชื่อมต่อ IBOutlet/IBAction ข้อผิดพลาดการตรวจสอบจะแสดงใน Issue Navigator ของ Xcode
ไฟล์เก็บถาวร .nib สุดท้ายจะถูกวางใน bundle ของแอปพลิเคชันในโฟลเดอร์ .nib (s) ขนาดของไฟล์ nib น้อยกว่า XML ดั้งเดิมอย่างมาก: รูปแบบไบนารีใช้ การแสดงที่ปรับให้เหมาะสม ด้วยการแทนที่สตริงด้วยโทเคนและการบีบอัดค่าตัวเลข การบีบอัดทั่วไปคือ 50–60% ของขนาด XML ดั้งเดิม
ในรันไทม์ nib จะถูกโหลดผ่าน UINib(nibName:bundle:) (k) หรือโดยอัตโนมัติผ่าน UIStoryboard.instantiateViewController(withIdentifier:) (k) กระบวนการโหลดรวมถึง:
awakeFromNib() (fn) สำหรับแต่ละออบเจกต์ — จุดเข้าเพื่อกำหนดค่าหลังโหลดเมธอด awakeFromNib() (fn) ถูกเรียกหลังจากตั้งค่า IBOutlet ทั้งหมดแล้ว แต่ก่อน layoutSubviews ครั้งแรก สะดวกสำหรับการกำหนดค่าเริ่มต้น: การตั้งค่ามุมมน, การเพิ่มเงา, การแปลข้อความ IBOutlet ทั้งหมดรับประกันว่าไม่ใช่ nil ใน awakeFromNib
ด้วยการเปิดตัว SwiftUI ในปี 2019 นักพัฒนา iOS ได้ทางเลือกอื่นแทน Interface Builder — เฟรมเวิร์กแบบประกาศพร้อม Canvas Preview แบบเรียลไทม์ มาวิเคราะห์ความแตกต่างหลักระหว่างสองแนวทางกัน
Interface Builder สร้างคำอธิบาย XML ที่คอมไพล์เป็น nib อินเทอร์เฟซถูกสร้างขึ้นด้วยภาพ โค้ดรับผิดชอบเฉพาะตรรกะ IB ต้องการเกณฑ์การเข้าที่ต่ำกว่าสำหรับนักออกแบบที่ไม่มีทักษะการเขียนโปรแกรม แต่ซับซ้อนในการตรวจสอบโค้ด (การเปลี่ยนแปลง XML ไม่เห็นใน diff)
SwiftUI Preview — การพัฒนาที่ใช้โค้ดทั้งหมด อินเทอร์เฟซอธิบายด้วย Swift ตัวอย่างจะอัปเดตทุกครั้งที่บันทึก ไม่มี XML, ไม่มี nib, ไม่มีความเสี่ยงที่การเชื่อมต่อ IBOutlet จะขาด SwiftUI Preview ทำงานเร็วกว่า IB เนื่องจากไม่ต้องคอมไพล์ไฟล์แยก
| เกณฑ์ | Interface Builder (UIKit) | SwiftUI Preview |
|---|---|---|
| รูปแบบไฟล์ | XML (.storyboard / .xib) → nib ไบนารี | โค้ด Swift (ไม่มีไฟล์กลาง) |
| ตัวอย่าง | ผืนผ้า IB หน่วงกับวิวซับซ้อน | Canvas Preview แบบเรียลไทม์ |
| รองรับเวอร์ชัน iOS | iOS 2.0+ (ทุกเวอร์ชัน) | iOS 13+ |
| Merge ใน git | มีปัญหา (ไฟล์ XML เดียว) | ง่าย (โค้ด Swift ปกติ) |
| ข้อมูลไดนามิก | ผ่าน IBOutlet + โค้ด | @State (k), @Observable (k) |
| วิวที่กำหนดเอง | @IBDesignable (คอมไพล์) | SwiftUI View พร้อม PreviewProvider |
| ประสิทธิภาพ | โหลด nib เร็ว | คอมไพล์ Swift ทันที |
ในทางปฏิบัติ การเลือกระหว่าง IB และ SwiftUI Preview ขึ้นอยู่กับข้อกำหนดของโปรเจกต์ Interface Builder ขาดไม่ได้สำหรับแอปพลิเคชัน UIKit ที่รองรับ iOS รุ่นเก่า และสำหรับโปรเจกต์เชิงพาณิชย์ที่นักออกแบบทำงานใน Xcode โดยไม่มีทักษะ Swift SwiftUI เหมาะกว่าสำหรับโปรเจกต์ใหม่ที่เน้น iOS 17+ ซึ่งความเร็วในการพัฒนาและการตอบสนองมีความสำคัญ
Apple ไม่มีแผนที่จะลบ Interface Builder ออกจาก Xcode ยิ่งไปกว่านั้น ใน Xcode 16 บริษัทได้ปรับปรุงประสิทธิภาพของผืนผ้าใบ IB และเพิ่มการรองรับคอมโพเนนต์ SwiftUI ผ่าน UIViewRepresentable Bridge คาดว่า IB จะได้รับการสนับสนุนอย่างน้อยจนถึงปี 2030
ประสบการณ์หลายปีในการพัฒนา iOS ได้สร้างชุดคำแนะนำที่ลดจำนวนปัญหาเมื่อใช้ Interface Builder ในโปรเจกต์เชิงพาณิชย์
ใช้ XIB แทน Storyboard สำหรับคอมโพเนนต์ที่ใช้ซ้ำได้ แต่ละเซลล์ตารางที่กำหนดเอง ส่วนหัวหรือส่วนท้ายควรอยู่ใน XIB แยกต่างหาก ซึ่งช่วยให้ merge ง่ายขึ้น เพิ่มความเร็วในการโหลด และช่วยให้ใช้คอมโพเนนต์ซ้ำระหว่างโปรเจกต์ผ่าน Swift Package Manager หรือ CocoaPods
กำหนดค่า Storyboard References เพื่อแบ่ง storyboard ขนาดใหญ่ออกเป็นโมดูล แทนที่จะใช้ Main.storyboard เดียวที่มี 100 หน้าจอ ให้สร้าง storyboard หนึ่งอันต่อโมดูล (Auth, Profile, Feed) และเชื่อมต่อผ่าน Storyboard Reference ซึ่งจะลดเวลาในการคอมไพล์ ibtool และทำให้การทำงานเป็นทีมง่ายขึ้น
หลีกเลี่ยงการเชื่อมต่อ IBOutlet ไปยัง File's Owner (k) โดยไม่ตรวจสอบ แต่ละการเชื่อมต่อควรเป็น weak (k) และเป็นทางเลือก (implicitly unwrapped optional ดีเฉพาะใน playground) เมื่อเปลี่ยนชื่อ IBOutlet ในวิว Xcode จะอัปเดตการเชื่อมต่อโดยอัตโนมัติ แต่เมื่อแก้ไข XML ด้วยตนเอง ง่ายที่จะทำผิดพลาด
Show Connection Panel (k) เสมอหลังจากแก้ไขไฟล์ IB — ตัวบ่งชี้สีแดงแสดงการเชื่อมต่อที่ขาดUser Defined Runtime Attributes (k) เพื่อตั้งค่าคุณสมบัติโดยไม่ต้องเขียนโค้ด: layer.cornerRadius, layer.borderWidth, tintColorIdentifier (k) ให้แต่ละข้อจำกัดใน Size Inspector — ช่วยในการดีบักเมื่อเกิดข้อขัดแย้งimport UIKit
final class ProfileHeaderView: UIView {
@IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var bioLabel: UILabel!
@IBOutlet weak var editButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
avatarImageView.layer.cornerRadius = avatarImageView.bounds.width / 2
avatarImageView.layer.masksToBounds = true
nameLabel.font = UIFont.preferredFont(forTextStyle: .headline)
bioLabel.font = UIFont.preferredFont(forTextStyle: .subheadline)
}
func configure(with profile: UserProfile) {
nameLabel.text = profile.fullName
bioLabel.text = profile.bio
/// โหลดอวาตาร์ผ่าน SDWebImage หรือ Kingfisher
}
static func instantiateFromNib() -> ProfileHeaderView {
let nib = UINib(nibName: String(describing: self), bundle: nil)
return nib.instantiate(withOwner: nil).first as! ProfileHeaderView
}
}ในตัวอย่างแสดงแนวทางปฏิบัติที่ดีที่สุดสำหรับวิว XIB: เมธอดแบบคงที่ instantiateFromNib (fn) โหลดวิวจาก XIB ที่มีชื่อเดียวกับคลาส เมธอด awakeFromNib (fn) กำหนดค่า UI (มุมมน, แบบอักษร) และเมธอด configure(with:) (fn) รับโมเดลข้อมูลเพื่อกรอกข้อมูล การแยกหน้าที่รับผิดชอบช่วยลดความซับซ้อนในการทดสอบและการใช้ซ้ำ
คำถามที่พบบ่อย
Interface Builder — โปรแกรมแก้ไขแบบภาพสำหรับ UIKit ด้วยรูปแบบ Storyboard/XIB ทำงานผ่าน drag-and-drop SwiftUI Preview — ตัวอย่างแบบประกาศเรียลไทม์ที่อินเทอร์เฟซอธิบายด้วยโค้ด Swift ทั้งสองเครื่องมือรวมอยู่ใน Xcode แต่ IB สร้าง XML ในขณะที่ SwiftUI คอมไพล์ Swift โดยตรง IB รองรับ iOS 2.0+, SwiftUI — iOS 13+
ไม่ Interface Builder ไม่เข้ากันได้โดยตรงกับ SwiftUI SwiftUI ใช้ไวยากรณ์แบบประกาศและ Canvas Preview ของตัวเอง อย่างไรก็ตาม โปรเจกต์ UIKit ที่สร้างผ่าน IB สามารถรวมเข้ากับ SwiftUI ผ่าน UIViewRepresentable และวิว SwiftUI สามารถฝังใน UIKit ผ่าน UIHostingController ซึ่งช่วยให้ย้ายจาก IB ไป SwiftUI ได้อย่างค่อยเป็นค่อยไป
@IBDesignable — คำอธิบายประกอบ Swift ที่แสดง UIView ที่กำหนดเองใน Interface Builder แบบเรียลไทม์โดยไม่ต้องเปิดแอปพลิเคชัน @IBInspectable — คำอธิบายประกอบสำหรับคุณสมบัติที่เพิ่มลงในแผง Attributes Inspector ของ IB คำอธิบายประกอบทั้งสองเร่งการพัฒนาคอมโพเนนต์ UI ที่กำหนดเอง: เพียงเปลี่ยนคุณสมบัติในตัวตรวจสอบ — การเปลี่ยนแปลงจะเห็นบนผืนผ้าใบทันที
Auto Layout ใน Interface Builder กำหนดข้อจำกัดผ่านเมนู Pin (ระยะขอบ, ความกว้าง, ความสูง) และ Align (การจัดกึ่งกลาง, baseline) ข้อจำกัดแต่ละข้อคือความสัมพันธ์ทางคณิตศาสตร์ระหว่างวิว IB แสดงข้อผิดพลาดด้วยเส้นสีแดงและข้อขัดแย้งด้วยคำเตือนสีเหลือง Size Classes ใน IB ช่วยให้กำหนดข้อจำกัดที่แตกต่างกันสำหรับอุปกรณ์และการวางแนวที่แตกต่างกันโดยไม่ต้องเขียนโค้ด
IBOutlet — คำอธิบายประกอบสำหรับการอ้างอิงถึงองค์ประกอบ UI จากโค้ด (เช่น @IBOutlet weak var label: UILabel!) IBAction — คำอธิบายประกอบสำหรับเมธอดที่ถูกเรียกเมื่อเกิดเหตุการณ์ (เช่น @IBAction func buttonTapped(_ sender: UIButton)) การเชื่อมต่อถูกสร้างขึ้นผ่าน Ctrl+drag จากผืนผ้าใบ IB ไปยังไฟล์ตัวควบคุม Xcode จะสร้างโค้ดการเชื่อมต่อโดยอัตโนมัติเมื่อปล่อยเมาส์
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ