Interface Builder — trình chỉnh sửa giao diện trực quan được tích hợp trong Xcode để phát triển iOS và macOS. Cho phép tạo UI qua drag-and-drop, cấu hình Auto Layout, kết nối mã qua IBOutlet và IBAction. Chúng ta sẽ phân tích cách IB hoạt động, sự khác biệt giữa Storyboard và XIB, và tại sao cần @IBDesignable.
Những điểm chính
Interface Builder — một thành phần của Xcode được thiết kế để thiết kế giao diện người dùng trực quan. Lịch sử của IB bắt đầu từ năm 1988 tại công ty NeXT, rất lâu trước khi iOS ra đời. Stefan Pope đã phát triển phiên bản đầu tiên cho NeXTSTEP — hệ điều hành đã trở thành nền tảng của macOS và iOS. Năm 1996, Apple mua lại NeXT và tích hợp Interface Builder vào Xcode.
Trong Xcode hiện đại, Interface Builder hỗ trợ ba định dạng tệp: Storyboard, XIB (Xcode Interface Builder) và tệp XIB cho ô bảng và view tùy chỉnh. Mỗi định dạng này lưu trữ mô tả XML về hệ thống phân cấp các phần tử UI, thuộc tính, ràng buộc và kết nối với mã.
IB hoạt động ở cấp độ UIKit: các nút, nhãn, trường văn bản, bảng, bộ sưu tập và ràng buộc được kéo bằng chuột lên khung vẽ. Xcode biên dịch các tệp .storyboard và .xib thành kho lưu trữ nib (compiled Interface Builder) ở giai đoạn build, giảm kích thước bundle và tăng tốc tải.
Theo Apple, hơn 70% dự án iOS trên UIKit sử dụng Interface Builder ở các giai đoạn phát triển khác nhau. Bất chấp sự phát triển của SwiftUI, IB vẫn là tiêu chuẩn cho các ứng dụng thương mại hỗ trợ iOS 12 trở xuống, cũng như cho các giao diện tùy chỉnh phức tạp yêu cầu tinh chỉnh Auto Layout.
Trước Xcode 4, Interface Builder là một ứng dụng riêng biệt, chạy song song với trình soạn thảo mã. Trong Xcode 4 (2011), Apple đã hợp nhất IB và trình soạn thảo mã thành một IDE duy nhất. Điều này cho phép chuyển đổi giữa mã và layout mà không cần chuyển cửa sổ, cũng như xem các thay đổi thuộc tính trong thời gian thực qua bảng Attributes Inspector.
| Phiên bản Xcode | Năm | Thay đổi trong Interface Builder |
|---|---|---|
| Xcode 3 | 2008 | IB — ứng dụng riêng, hỗ trợ iOS 2.0 |
| Xcode 4 | 2011 | IB được tích hợp vào IDE, Storyboard xuất hiện |
| Xcode 5 | 2013 | Auto Layout với menu ràng buộc, xem trước màn hình |
| Xcode 6 | 2014 | Size Classes, @IBDesignable, Preview Assistant |
| Xcode 11 | 2019 | SwiftUI Canvas, IB vẫn dành cho UIKit |
| Xcode 15 | 2023 | SwiftUI Preview là công cụ chính, IB ở chế độ kế thừa |
Với sự ra đời của SwiftUI vào năm 2019, Apple đã chuyển trọng tâm sang phát triển khai báo, tuy nhiên Interface Builder vẫn được tích hợp trong Xcode để hỗ trợ các dự án UIKit. Hàng nghìn ứng dụng hiện có tiếp tục sử dụng IB và Apple chưa thông báo về việc loại bỏ nó.
Interface Builder hỗ trợ hai định dạng chính: Storyboard (.storyboard) và XIB (.xib). Sự khác biệt giữa chúng nằm ở phạm vi và kịch bản sử dụng.
Storyboard — tệp chứa toàn bộ cảnh của ứng dụng: nhiều màn hình (UIViewController), chuyển tiếp giữa chúng (segues), bộ điều khiển điều hướng, thanh tab và tất cả các phần tử UI. Storyboard được tải một lần khi khởi động từ Info.plist qua khóa UIMainStoryboardFile (k). Nó thuận tiện để trực quan hóa luồng màn hình, nhưng gây ra vấn đề khi xung đột merge trong git vì mô tả XML của toàn bộ ứng dụng được lưu trong một tệp duy nhất.
XIB (viết tắt của Xcode Interface Builder) — tệp cho một thành phần duy nhất: một UIView riêng, UITableViewCell, UICollectionViewCell hoặc một ViewController. XIB được tải theo yêu cầu qua UINib(nibName:bundle:) (k) hoặc phương thức Bundle.loadNibNamed (k). Các tệp XIB dễ merge hơn, nhỏ gọn hơn và tải nhanh hơn vì chúng không chứa mô tả toàn bộ ứng dụng.
| Tiêu chí | Storyboard | XIB |
|---|---|---|
| Phạm vi | Nhiều màn hình + chuyển tiếp | Một màn hình hoặc thành phần |
| Segues | Hỗ trợ (push, modal, unwind) | Không hỗ trợ |
| Merge trong git | Phức tạp (một XML lớn) | Đơn giản (nhiều tệp nhỏ) |
| Tải | Khi khởi động ứng dụng | Theo yêu cầu (lười biếng) |
| Tái sử dụng | Chỉ qua storyboard references | Cao (ô, header, view) |
| Khuyến nghị của Apple | Không khuyến nghị cho dự án lớn | Khuyến nghị cho các thành phần |
Từ phiên bản Xcode 11, Apple khuyến nghị sử dụng XIB cho các thành phần riêng lẻ và tránh Storyboard nguyên khối. Để điều hướng giữa các màn hình, ưu tiên điều hướng bằng mã qua UIStoryboardSegue (k) thủ công hoặc bộ điều phối.
Các tệp .storyboard và .xib lưu trữ XML ở định dạng Interface Builder Cocoa Touch XIB (dt). Ví dụ về cấu trúc đơn giản hóa:
<!-- Tệp XIB với UIView và 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="Tiêu đề"
textColor="darkTextColor"
fontDescription="title1"/>
</subviews>
</view>
</objects>
</document>Mỗi phần tử có id duy nhất (an), qua đó IB kết nối nút XML với đối tượng runtime. Khi biên dịch, Xcode chuyển đổi XML thành định dạng nib nhị phân (.nib), giảm kích thước tệp khoảng 40%.
Auto Layout — hệ thống định vị các phần tử trên màn hình thông qua các mối quan hệ toán học (ràng buộc). Interface Builder cung cấp giao diện trực quan để tạo, chỉnh sửa và gỡ lỗi các ràng buộc mà không cần viết mã. Mỗi ràng buộc mô tả một sự phụ thuộc: view.leading = superview.leading + 16 (k) hoặc view.width = 2 * otherView.height (k).
Trong IB, các ràng buộc được tạo qua menu Pin (cố định lề, chiều rộng, chiều cao) và Align (căn giữa, cạnh, baseline). Bảng Size Inspector hiển thị tất cả các ràng buộc của phần tử đã chọn, mức độ ưu tiên của chúng (required/high/low) và cho phép chỉnh sửa hệ số và hằng số.
IB cũng hỗ trợ UIStackView — vùng chứa tự động quản lý vị trí của các view con. Chỉ cần đặt các phần tử vào stack view trên khung vẽ và IB sẽ tự động tạo các ràng buộc cần thiết. Điều này tăng tốc đáng kể việc bố trí so với việc đặt ràng buộc thủ công.
Size Classes — sự trừu tượng hóa nhóm các thiết bị theo chiều rộng và chiều cao màn hình: Compact và Regular. Các kết hợp (wC hR cho iPhone dọc, wR hR cho iPad) cho phép đặt các ràng buộc và bố trí phần tử khác nhau cho các kịch bản khác nhau. Trong Interface Builder, việc chuyển đổi giữa các size class sẽ thay đổi tập hợp các ràng buộc đang hoạt động trên khung vẽ.
| Thiết bị | Hướng | Width Class | Height Class |
|---|---|---|---|
| iPhone (trừ Max/Plus) | Dọc | Compact | Regular |
| iPhone (trừ Max/Plus) | Ngang | Compact | Compact |
| iPhone Plus/Max | Ngang | Regular | Compact |
| iPad | Bất kỳ | Regular | Regular |
| iPad Split View | 1/3 màn hình | Compact | Regular |
Ví dụ về ràng buộc với biến thể theo 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()
}
}
}Trong mã trên, traitCollectionDidChange phản ứng với sự thay đổi size class, cập nhật ràng buộc và phông chữ. Interface Builder cho phép đặt giá trị mặc định cho mỗi size class qua trình kiểm tra và mã được sử dụng cho các kịch bản động không thể mô tả tĩnh.
Kết nối giữa giao diện trực quan trong Interface Builder và mã Swift/Objective-C được thực hiện qua hai cơ chế: IBOutlet (Interface Builder Outlet) và IBAction (Interface Builder Action). Cả hai đều được tạo bằng cách kéo từ khung vẽ IB vào tệp điều khiển với phím Ctrl được giữ.
IBOutlet — chú thích khai báo tham chiếu đến phần tử UI. Xcode tự động kết nối nó với đối tượng tương ứng trong kho lưu trữ nib khi tải. Nếu kết nối bị đứt (ví dụ: phần tử bị đổi tên), ứng dụng sẽ gặp sự cố với lỗi NSUnknownKeyException (k). IBOutlet được đánh dấu là weak (k) vì nib sở hữu đối tượng và bộ điều khiển chỉ là người quan sát.
IBAction — phương thức được gọi khi xảy ra sự kiện phần tử UI: nhấn nút, thay đổi văn bản, chuyển đổi công tắc. IB kết nối UIControlEvent (k) với phương thức qua addTarget:action:forControlEvents: (k). Trong mã, IBAction xuất hiện dưới dạng phương thức thông thường với kiểu trả về 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: "Vui lòng điền tất cả các trường")
return
}
loginButton.isEnabled = false
spinner.startAnimating()
performLogin(email: email, password: password)
}
private func performLogin(email: String, password: String) {
/// Gọi API qua 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: "Lỗi",
message: message,
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}Trong ví dụ hiển thị kết nối tiêu chuẩn: IBOutlet cho các trường văn bản, nút và spinner, IBAction để xử lý nhấn nút. Tất cả các kết nối này được thiết lập trong Interface Builder qua Ctrl+drag. Nếu kết nối không được cấu hình, IBOutlet sẽ là nil (v) tại runtime, gây ra sự cố khi truy cập — do đó IBOutlet được khai báo là weak var (k s) với implicit unwrap.
@IBDesignable — chú thích Swift cho phép hiển thị UIView tùy chỉnh trực tiếp trên khung vẽ Interface Builder trong thời gian thực. Nhà phát triển thấy kết quả của các thay đổi mã mà không cần khởi chạy ứng dụng. @IBInspectable — chú thích cho các thuộc tính, thêm chúng vào bảng Attributes Inspector của IB, nơi có thể thay đổi giá trị tương tác.
Các chú thích này đặc biệt hữu ích khi tạo thư viện thành phần UI: nút tùy chỉnh, trường nhập có mặt nạ, chỉ báo hoạt ảnh. IBDesignable sử dụng prepareForInterfaceBuilder() (fn) để biên dịch riêng mã build, không ảnh hưởng đến tệp nhị phân chính của ứng dụng.
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()
}
}Trong mã trên, GradientButton là thành phần IBDesignable với các thuộc tính IBInspectable startColor (v), endColor (v) và cornerRadius (v). Khi kéo UIView lên khung vẽ IB và thay đổi lớp thành GradientButton trong Identity Inspector, một nút có gradient sẽ hiển thị trên khung vẽ trong thời gian thực. Tất cả các thuộc tính IBInspectable sẽ xuất hiện trong bảng Attributes Inspector bên phải.
Quan trọng: @IBDesignable biên dịch toàn bộ mã để hiển thị trong IB, do đó không nên thực hiện các yêu cầu mạng hoặc thao tác dài bên trong nó. Để phân biệt, sử dụng #if TARGET_INTERFACE_BUILDER (k) — biên dịch có điều kiện loại trừ mã không dành cho IB.
Quá trình chuyển đổi các tệp Interface Builder từ tạo nib đến hiển thị trên màn hình bao gồm nhiều giai đoạn. Hiểu chu trình này giúp chẩn đoán các sự cố với IB.
Ở giai đoạn build, Xcode chạy công cụ ibtool (k, fn) — tiện ích dòng lệnh để biên dịch các tệp .storyboard và .xib thành định dạng nib nhị phân. ibtool cũng thực hiện xác thực: kiểm tra tính chính xác của các ràng buộc, sự tồn tại của tất cả các lớp, loại kết nối IBOutlet/IBAction. Lỗi xác thực được hiển thị trong Issue Navigator của Xcode.
Kho lưu trữ .nib cuối cùng được đặt trong bundle ứng dụng trong thư mục .nib (s). Kích thước tệp nib nhỏ hơn đáng kể so với XML gốc: định dạng nhị phân sử dụng biểu diễn được tối ưu hóa với việc thay thế chuỗi bằng token và nén các giá trị số. Nén điển hình là 50–60% so với kích thước XML gốc.
Trong runtime, nib được tải qua UINib(nibName:bundle:) (k) hoặc tự động qua UIStoryboard.instantiateViewController(withIdentifier:) (k). Quá trình tải bao gồm:
awakeFromNib() (fn) cho mỗi đối tượng — điểm vào để cấu hình sau tảiPhương thức awakeFromNib() (fn) được gọi sau khi tất cả IBOutlet đã được đặt, nhưng trước layoutSubviews đầu tiên. Điều này thuận tiện cho cấu hình ban đầu: đặt bo tròn, thêm bóng, bản địa hóa văn bản. Tuy nhiên, tất cả IBOutlet đều được đảm bảo không nil trong awakeFromNib.
Với sự ra mắt của SwiftUI vào năm 2019, các nhà phát triển iOS đã có một giải pháp thay thế cho Interface Builder — framework khai báo với Canvas Preview thời gian thực. Hãy phân tích sự khác biệt chính giữa hai cách tiếp cận.
Interface Builder tạo mô tả XML được biên dịch thành nib. Giao diện được tạo trực quan, mã chỉ chịu trách nhiệm về logic. IB yêu cầu ngưỡng đầu vào thấp hơn cho các nhà thiết kế không có kỹ năng lập trình, nhưng phức tạp trong việc xem xét mã (các thay đổi XML không hiển thị trong diff).
SwiftUI Preview — phát triển hoàn toàn dựa trên mã. Giao diện được mô tả bằng Swift, bản xem trước cập nhật sau mỗi lần lưu. Không có XML, không có nib, không có rủi ro đứt kết nối IBOutlet. SwiftUI Preview hoạt động nhanh hơn IB vì không yêu cầu biên dịch tệp riêng.
| Tiêu chí | Interface Builder (UIKit) | SwiftUI Preview |
|---|---|---|
| Định dạng tệp | XML (.storyboard / .xib) → nib nhị phân | Mã Swift (không có tệp trung gian) |
| Xem trước | Khung vẽ IB có độ trễ với view phức tạp | Canvas Preview thời gian thực |
| Hỗ trợ phiên bản iOS | iOS 2.0+ (tất cả phiên bản) | iOS 13+ |
| Merge trong git | Có vấn đề (một tệp XML duy nhất) | Đơn giản (mã Swift thông thường) |
| Dữ liệu động | Qua IBOutlet + mã | @State (k), @Observable (k) |
| View tùy chỉnh | @IBDesignable (biên dịch) | SwiftUI View với PreviewProvider |
| Hiệu suất | Tải nib nhanh | Biên dịch Swift tức thì |
Trong thực tế, việc lựa chọn giữa IB và SwiftUI Preview phụ thuộc vào yêu cầu dự án. Interface Builder không thể thiếu cho các ứng dụng UIKit hỗ trợ iOS cũ, cũng như cho các dự án thương mại nơi các nhà thiết kế làm việc trong Xcode mà không có kỹ năng Swift. SwiftUI được ưu tiên cho các dự án mới nhắm đến iOS 17+, nơi tốc độ phát triển và khả năng phản hồi là quan trọng.
Apple không có kế hoạch loại bỏ Interface Builder khỏi Xcode. Hơn nữa, trong Xcode 16, công ty đã cải thiện hiệu suất của khung vẽ IB và thêm hỗ trợ cho các thành phần SwiftUI qua UIViewRepresentable Bridge. Dự kiến IB sẽ được hỗ trợ ít nhất đến năm 2030.
Kinh nghiệm nhiều năm phát triển iOS đã hình thành một loạt khuyến nghị giúp giảm số lượng vấn đề khi sử dụng Interface Builder trong các dự án thương mại.
Sử dụng XIB thay vì Storyboard cho các thành phần tái sử dụng. Mỗi ô bảng tùy chỉnh, header hoặc footer phải nằm trong một XIB riêng. Điều này giúp merge dễ dàng hơn, tăng tốc tải và cho phép tái sử dụng các thành phần giữa các dự án qua Swift Package Manager hoặc CocoaPods.
Cấu hình Storyboard References để chia các storyboard lớn thành các mô-đun. Thay vì một Main.storyboard duy nhất với 100 màn hình, hãy tạo một storyboard cho mỗi mô-đun (Auth, Profile, Feed) và kết nối chúng qua Storyboard Reference. Điều này sẽ giảm thời gian biên dịch ibtool và đơn giản hóa công việc nhóm.
Tránh kết nối IBOutlet đến File's Owner (k) mà không kiểm tra. Mỗi kết nối phải là weak (k) và tùy chọn (implicitly unwrapped optional chỉ tốt trong playground). Khi đổi tên IBOutlet trong view, Xcode tự động cập nhật kết nối, nhưng khi chỉnh sửa thủ công XML, rất dễ mắc lỗi.
Show Connection Panel (k) sau khi chỉnh sửa tệp IB — chỉ báo màu đỏ cho biết kết nối bị đứtUser Defined Runtime Attributes (k) để đặt thuộc tính mà không cần mã: layer.cornerRadius, layer.borderWidth, tintColorIdentifier (k) cho mỗi ràng buộc trong Size Inspector — giúp gỡ lỗi khi xung độtimport 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
/// Tải avatar qua SDWebImage hoặc Kingfisher
}
static func instantiateFromNib() -> ProfileHeaderView {
let nib = UINib(nibName: String(describing: self), bundle: nil)
return nib.instantiate(withOwner: nil).first as! ProfileHeaderView
}
}Trong ví dụ hiển thị phương pháp hay nhất cho view XIB: phương thức tĩnh instantiateFromNib (fn) tải view từ XIB có cùng tên với lớp. Phương thức awakeFromNib (fn) cấu hình UI (bo tròn, phông chữ) và phương thức configure(with:) (fn) nhận mô hình dữ liệu để điền. Phân chia trách nhiệm giúp đơn giản hóa việc kiểm thử và tái sử dụng.
Câu hỏi thường gặp
Interface Builder — trình chỉnh sửa trực quan cho UIKit với định dạng Storyboard/XIB, hoạt động qua drag-and-drop. SwiftUI Preview — bản xem trước khai báo thời gian thực, nơi giao diện được mô tả bằng mã Swift. Cả hai công cụ đều được tích hợp trong Xcode, nhưng IB tạo XML còn SwiftUI biên dịch Swift trực tiếp. IB hỗ trợ iOS 2.0+, SwiftUI — iOS 13+.
Không, Interface Builder không tương thích trực tiếp với SwiftUI. SwiftUI sử dụng cú pháp khai báo riêng và Canvas Preview. Tuy nhiên, các dự án UIKit được tạo qua IB có thể được tích hợp vào SwiftUI thông qua UIViewRepresentable và các view SwiftUI có thể được nhúng vào UIKit thông qua UIHostingController. Điều này cho phép di chuyển dần từ IB sang SwiftUI.
@IBDesignable — chú thích Swift hiển thị UIView tùy chỉnh trực tiếp trong Interface Builder thời gian thực mà không cần khởi chạy ứng dụng. @IBInspectable — chú thích cho các thuộc tính, thêm chúng vào bảng Attributes Inspector của IB. Cả hai chú thích đều tăng tốc phát triển các thành phần UI tùy chỉnh: chỉ cần thay đổi thuộc tính trong trình kiểm tra — thay đổi sẽ hiển thị ngay trên khung vẽ.
Auto Layout trong Interface Builder đặt các ràng buộc qua menu Pin (lề, chiều rộng, chiều cao) và Align (căn giữa, baseline). Mỗi ràng buộc là một mối quan hệ toán học giữa các view. IB hiển thị lỗi bằng đường màu đỏ, xung đột bằng cảnh báo màu vàng. Size Classes trong IB cho phép đặt các ràng buộc khác nhau cho các thiết bị và hướng khác nhau mà không cần viết mã.
IBOutlet — chú thích cho tham chiếu đến phần tử UI từ mã (ví dụ: @IBOutlet weak var label: UILabel!). IBAction — chú thích cho phương thức được gọi khi có sự kiện (ví dụ: @IBAction func buttonTapped(_ sender: UIButton)). Kết nối được tạo qua Ctrl+drag từ khung vẽ IB vào tệp điều khiển. Xcode tự động tạo mã kết nối khi thả chuột.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm