Interface Builder — wizualny edytor interfejsów wbudowany w Xcode do tworzenia aplikacji na iOS i macOS. Pozwala tworzyć UI przez drag-and-drop, konfigurować Auto Layout, łączyć kod przez IBOutlet i IBAction. Omówimy, jak działa IB, czym różnią się Storyboard i XIB oraz do czego służą @IBDesignable.
Najważniejsze
Interface Builder — komponent Xcode przeznaczony do wizualnego projektowania interfejsów użytkownika. Historia IB rozpoczęła się w 1988 roku w firmie NeXT, na długo przed pojawieniem się iOS. Stefan Pope opracował pierwszą wersję dla NeXTSTEP — systemu operacyjnego, który stał się podstawą macOS i iOS. W 1996 roku Apple przejęła NeXT i zintegrowała Interface Builder z Xcode.
We współczesnym Xcode Interface Builder obsługuje trzy formaty plików: Storyboard, XIB (Xcode Interface Builder) oraz pliki XIB dla komórek tabel i niestandardowych widoków. Każdy z tych formatów przechowuje opis XML hierarchii elementów UI, ich właściwości, ograniczeń i połączeń z kodem.
IB działa na poziomie UIKit: przyciski, etykiety, pola tekstowe, tabele, kolekcje i ograniczenia są przeciągane myszą na płótno. Xcode kompiluje pliki .storyboard i .xib do archiwów nib (compiled Interface Builder) na etapie budowania, co zmniejsza rozmiar pakietu i przyspiesza ładowanie.
Według Apple ponad 70% projektów iOS na UIKit używa Interface Builder na różnych etapach tworzenia. Pomimo wzrostu SwiftUI, IB pozostaje standardem dla aplikacji komercyjnych obsługujących iOS 12 i starsze, a także dla złożonych niestandardowych interfejsów wymagających precyzyjnego dostrojenia Auto Layout.
Przed Xcode 4 Interface Builder był oddzielną aplikacją uruchamianą równolegle z edytorem kodu. W Xcode 4 (2011) Apple połączyła IB i edytor kodu w jedną IDE. Umożliwiło to przełączanie się między kodem a układem bez przełączania okien, a także podgląd zmian właściwości w czasie rzeczywistym przez panel Attributes Inspector.
| Wersja Xcode | Rok | Zmiany w Interface Builder |
|---|---|---|
| Xcode 3 | 2008 | IB — osobna aplikacja, obsługa iOS 2.0 |
| Xcode 4 | 2011 | IB wbudowany w IDE, pojawiły się Storyboard |
| Xcode 5 | 2013 | Auto Layout z menu ograniczeń, podgląd ekranów |
| Xcode 6 | 2014 | Size Classes, @IBDesignable, Preview Assistant |
| Xcode 11 | 2019 | SwiftUI Canvas, IB pozostaje dla UIKit |
| Xcode 15 | 2023 | SwiftUI Preview jako główne narzędzie, tryb legacy IB |
Wraz z pojawieniem się SwiftUI w 2019 roku Apple przesunęła nacisk na rozwój deklaratywny, jednak Interface Builder pozostaje wbudowany w Xcode do obsługi projektów UIKit. Tysiące istniejących aplikacji nadal używa IB, a Apple nie ogłosiła jego usunięcia.
Interface Builder obsługuje dwa główne formaty: Storyboard (.storyboard) i XIB (.xib). Różnica między nimi polega na zakresie widoczności i scenariuszu użycia.
Storyboard — plik zawierający całą scenę aplikacji: wiele ekranów (UIViewController), przejścia między nimi (segues), kontrolery nawigacyjne, paski zakładek i wszystkie elementy UI. Storyboard jest ładowany jednorazowo podczas uruchamiania z Info.plist przez klucz UIMainStoryboardFile (k). Jest to wygodne do wizualizacji przepływu ekranów, ale stwarza problemy przy konfliktach merge w git, ponieważ opis XML całej aplikacji jest przechowywany w jednym pliku.
XIB (skrót od Xcode Interface Builder) — plik dla jednego komponentu: pojedynczego UIView, UITableViewCell, UICollectionViewCell lub jednego ViewController. XIB jest ładowany na żądanie przez UINib(nibName:bundle:) (k) lub metodę Bundle.loadNibNamed (k). Pliki XIB są łatwiejsze do mergowania, bardziej kompaktowe i ładują się szybciej, ponieważ nie zawierają opisu całej aplikacji.
| Kryterium | Storyboard | XIB |
|---|---|---|
| Zakres | Wiele ekranów + przejścia | Jeden ekran lub komponent |
| Segues | Obsługuje (push, modal, unwind) | Nie obsługuje |
| Merge w git | Trudny (jeden duży XML) | Prosty (wiele małych plików) |
| Ładowanie | Przy starcie aplikacji | Na żądanie (leniwe) |
| Ponowne użycie | Tylko przez storyboard references | Wysokie (komórki, nagłówki, widoki) |
| Zalecenie Apple | Nie zalecany dla dużych projektów | Zalecany dla komponentów |
Od wersji Xcode 11 Apple zaleca używanie XIB dla pojedynczych komponentów i unikanie monolitycznych Storyboard. Do nawigacji między ekranami preferowana jest nawigacja kodem przez UIStoryboardSegue (k) ręcznie lub koordynatory.
Pliki .storyboard i .xib przechowują XML w formacie Interface Builder Cocoa Touch XIB (dt). Przykład uproszczonej struktury:
<!-- Plik XIB z UIView i 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="Nagłówek"
textColor="darkTextColor"
fontDescription="title1"/>
</subviews>
</view>
</objects>
</document>Każdy element ma unikalny id (an), przez który IB łączy węzeł XML z obiektem runtime. Podczas kompilacji Xcode przekształca XML w binarny format nib (.nib), zmniejszając rozmiar pliku o około 40%.
Auto Layout — system pozycjonowania elementów na ekranie przez relacje matematyczne (ograniczenia). Interface Builder udostępnia wizualny interfejs do tworzenia, edycji i debugowania ograniczeń bez pisania kodu. Każde ograniczenie opisuje zależność: view.leading = superview.leading + 16 (k) lub view.width = 2 * otherView.height (k).
W IB ograniczenia są tworzone przez menu Pin(przypinanie odstępów, szerokości, wysokości) i Align (wyrównywanie do środka, krawędzi, baseline). Panel Size Inspector pokazuje wszystkie ograniczenia wybranego elementu, ich priorytety (required/high/low) i umożliwia edycję mnożników i stałych.
IB obsługuje również UIStackView — kontener automatycznie zarządzający rozmieszczeniem widoków potomnych. Wystarczy umieścić elementy w stack view na płótnie, a IB wygeneruje niezbędne ograniczenia automatycznie. Znacznie przyspiesza to układanie w porównaniu do ręcznego ustawiania ograniczeń.
Size Classes — abstrakcja grupująca urządzenia według szerokości i wysokości ekranu: Compact i Regular. Kombinacje (wC hR dla iPhone portret, wR hR dla iPad) umożliwiają ustawianie różnych ograniczeń i rozmieszczenia elementów dla różnych scenariuszy. W Interface Builder przełączanie między size classes zmienia zestaw aktywnych ograniczeń na płótnie.
| Urządzenie | Orientacja | Width Class | Height Class |
|---|---|---|---|
| iPhone (oprócz Max/Plus) | Portret | Compact | Regular |
| iPhone (oprócz Max/Plus) | Krajobraz | Compact | Compact |
| iPhone Plus/Max | Krajobraz | Regular | Compact |
| iPad | Dowolna | Regular | Regular |
| iPad Split View | 1/3 ekranu | Compact | Regular |
Przykład ograniczenia z wariacją według 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()
}
}
}W powyższym kodzie traitCollectionDidChange reaguje na zmianę size class, aktualizując ograniczenie i czcionkę. Interface Builder umożliwia ustawienie domyślnych wartości dla każdego size class przez inspektor, a kod jest używany do dynamicznych scenariuszy, których nie można opisać statycznie.
Połączenie między wizualnym interfejsem w Interface Builder a kodem Swift/Objective-C odbywa się przez dwa mechanizmy: IBOutlet (Interface Builder Outlet) i IBAction (Interface Builder Action). Oba są tworzone przez przeciągnięcie z wciśniętym klawiszem Ctrl z płótna IB do pliku kontrolera.
IBOutlet — adnotacja deklarująca referencję do elementu UI. Xcode automatycznie łączy ją z odpowiednim obiektem w archiwum nib podczas ładowania. Jeśli połączenie zostanie przerwane (np. element został przemianowany), aplikacja ulega awarii z błędem NSUnknownKeyException (k). IBOutlet jest oznaczany jako weak (k), ponieważ nib jest właścicielem obiektu, a kontroler jest tylko obserwatorem.
IBAction — metoda wywoływana przy zdarzeniu elementu UI: naciśnięcie przycisku, zmiana tekstu, przełączenie przełącznika. IB łączy UIControlEvent (k) z metodą przez addTarget:action:forControlEvents: (k). W kodzie IBAction wygląda jak zwykła metoda z typem zwracanym 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: "Wypełnij wszystkie pola")
return
}
loginButton.isEnabled = false
spinner.startAnimating()
performLogin(email: email, password: password)
}
private func performLogin(email: String, password: String) {
/// Wywołanie API przez 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: "Błąd",
message: message,
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}Przykład pokazuje standardowe połączenie: IBOutlet dla pól tekstowych, przycisku i spinnera, IBAction do obsługi naciśnięcia. Wszystkie te połączenia są ustawiane w Interface Builder przez Ctrl+drag. Jeśli połączenie nie jest skonfigurowane, IBOutlet będzie nil (v) w runtime, co spowoduje crash przy odwołaniu — dlatego IBOutlet jest deklarowany jako weak var (k s) z implicit unwrap.
@IBDesignable — adnotacja Swift umożliwiająca wyświetlanie niestandardowego UIView bezpośrednio na płótnie Interface Builder w czasie rzeczywistym. Deweloper widzi efekt zmian kodu bez uruchamiania aplikacji. @IBInspectable — adnotacja dla właściwości, dodająca je do panelu Attributes Inspector IB, gdzie można zmieniać wartości interaktywnie.
Te adnotacje są szczególnie przydatne przy tworzeniu bibliotek komponentów UI: niestandardowe przyciski, pola wprowadzania z maską, animowane wskaźniki. IBDesignable używa prepareForInterfaceBuilder() (fn) do oddzielnej kompilacji kodu budowania, nie wpływającej na główny plik binarny aplikacji.
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()
}
}W powyższym kodzie GradientButton to komponent IBDesignable z właściwościami IBInspectable startColor (v), endColor (v) i cornerRadius (v). Po przeciągnięciu UIView na płótno IB i zmianie klasy na GradientButton w Identity Inspector, na płótnie wyświetli się przycisk z gradientem w czasie rzeczywistym. Wszystkie właściwości IBInspectable pojawią się w panelu Attributes Inspector po prawej.
Ważne: @IBDesignable kompiluje kod w całości do wyświetlenia w IB, dlatego nie powinien wykonywać zapytań sieciowych ani długich operacji. Do rozróżnienia używa się #if TARGET_INTERFACE_BUILDER (k) — kompilacji warunkowej, wykluczającej kod nieprzeznaczony dla IB.
Proces transformacji plików Interface Builder od utworzenia nib do wyświetlenia na ekranie obejmuje kilka etapów. Zrozumienie tego cyklu pomaga diagnozować problemy z IB.
Na etapie budowania Xcode uruchamia narzędzie ibtool (k, fn) — narzędzie wiersza poleceń do kompilacji plików .storyboard i .xib do binarnego formatu nib. ibtool wykonuje również walidację: sprawdza poprawność ograniczeń, obecność wszystkich klas, typy połączeń IBOutlet/IBAction. Błędy walidacji są wyświetlane w Issue Navigator Xcode.
Końcowe archiwum .nib jest umieszczane w pakiecie aplikacji w folderze .nib (s). Rozmiar pliku nib jest znacznie mniejszy niż oryginalnego XML: format binarny używa zoptymalizowanej reprezentacji z zamianą ciągów na tokeny i kompresją wartości liczbowych. Typowa kompresja wynosi 50–60% oryginalnego rozmiaru XML.
W runtime nib jest ładowany przez UINib(nibName:bundle:) (k) lub automatycznie przez UIStoryboard.instantiateViewController(withIdentifier:) (k). Proces ładowania obejmuje:
awakeFromNib() (fn) dla każdego obiektu — punkt wejścia do konfiguracji po załadowaniuMetoda awakeFromNib() (fn) jest wywoływana po ustawieniu wszystkich IBOutlet, ale przed pierwszym layoutSubviews. Jest to wygodne do początkowej konfiguracji: ustawiania zaokrągleń, dodawania cieni, lokalizacji tekstu. Jednak wszystkie IBOutlet są już gwarantowanie nie nil w awakeFromNib.
Wraz z wydaniem SwiftUI w 2019 roku deweloperzy iOS otrzymali alternatywę dla Interface Builder — deklaratywny framework z Canvas Preview w czasie rzeczywistym. Omówimy kluczowe różnice między tymi dwoma podejściami.
Interface Builder generuje opis XML, który jest kompilowany do nib. Interfejs jest tworzony wizualnie, kod odpowiada tylko za logikę. IB ma niższy próg wejścia dla projektantów bez umiejętności programowania, ale jest trudny w przeglądzie kodu (zmiany XML nie są widoczne w diff).
SwiftUI Preview — w pełni kodowe tworzenie. Interfejs jest opisywany w Swift, podgląd aktualizuje się przy każdym zapisie. Nie ma XML, nie ma nib, nie ma ryzyka przerwania połączeń IBOutlet. SwiftUI Preview działa szybciej niż IB, ponieważ nie wymaga kompilacji osobnego pliku.
| Kryterium | Interface Builder (UIKit) | SwiftUI Preview |
|---|---|---|
| Format pliku | XML (.storyboard / .xib) → binarny nib | Kod Swift (bez pliku pośredniego) |
| Podgląd | Płótno IB z opóźnieniem przy złożonych widokach | Canvas Preview w czasie rzeczywistym |
| Obsługa wersji iOS | iOS 2.0+ (wszystkie wersje) | iOS 13+ |
| Merge w git | Problematyczny (jeden plik XML) | Prosty (zwykły kod Swift) |
| Dane dynamiczne | Przez IBOutlet + kod | @State (k), @Observable (k) |
| Niestandardowe widoki | @IBDesignable (kompilacja) | SwiftUI View z PreviewProvider |
| Wydajność | Szybkie ładowanie nib | Kompilacja Swift na bieżąco |
W praktyce wybór między IB a SwiftUI Preview zależy od wymagań projektu. Interface Builder jest niezastąpiony dla aplikacji UIKit obsługujących stare iOS, a także dla projektów komercyjnych, gdzie projektanci pracują w Xcode bez znajomości Swift. SwiftUI jest preferowany dla nowych projektów ukierunkowanych na iOS 17+, gdzie liczy się szybkość tworzenia i reaktywność.
Apple nie planuje usuwać Interface Builder z Xcode. Co więcej, w Xcode 16 firma poprawiła wydajność płótna IB i dodała obsługę komponentów SwiftUI przez most UIViewRepresentable. Oczekuje się, że IB będzie wspierany jeszcze co najmniej do 2030 roku.
Wieloletnie doświadczenie w tworzeniu aplikacji iOS ukształtowało zestaw zaleceń zmniejszających liczbę problemów przy używaniu Interface Builder w projektach komercyjnych.
Używaj XIB zamiast Storyboard dla komponentów wielokrotnego użytku. Każda niestandardowa komórka tabeli, nagłówek lub stopka powinny być w osobnym XIB. Ułatwia to merge, przyspiesza ładowanie i umożliwia ponowne wykorzystanie komponentów między projektami przez Swift Package Manager lub CocoaPods.
Konfiguruj Storyboard References do dzielenia dużych storyboardów na moduły. Zamiast jednego Main.storyboard na 100 ekranów utwórz osobny storyboard dla każdego modułu (Auth, Profile, Feed) i połącz je przez Storyboard Reference. Zmniejszy to czas kompilacji ibtool i uprości pracę zespołową.
Unikaj połączeń IBOutlet do File's Owner (k) bez sprawdzenia. Każde połączenie powinno być weak (k) i opcjonalne (implicitly unwrapped optional jest świetny tylko w playground). Przy zmianie nazwy IBOutlet w widoku Xcode automatycznie aktualizuje połączenie, ale przy ręcznej edycji XML łatwo popełnić błąd.
Show Connection Panel (k) po edycji pliku IB — czerwone wskaźniki oznaczają przerwane połączeniaUser Defined Runtime Attributes (k) do ustawiania właściwości bez kodu: layer.cornerRadius, layer.borderWidth, tintColorIdentifier (k) każdemu ograniczeniu w Size Inspector — pomaga to w debugowaniu przy konfliktachimport 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
/// Ładowanie awatara przez SDWebImage lub Kingfisher
}
static func instantiateFromNib() -> ProfileHeaderView {
let nib = UINib(nibName: String(describing: self), bundle: nil)
return nib.instantiate(withOwner: nil).first as! ProfileHeaderView
}
}Przykład pokazuje best practice dla widoku XIB: statyczna metoda instantiateFromNib (fn) ładuje widok z XIB o tej samej nazwie co klasa. Metoda awakeFromNib (fn) konfiguruje UI (zaokrąglenia, czcionki), a metoda configure(with:) (fn) przyjmuje model danych do wypełnienia. Podział odpowiedzialności ułatwia testowanie i ponowne wykorzystanie.
Często zadawane pytania
Interface Builder — wizualny edytor dla UIKit z formatem Storyboard/XIB, działający przez drag-and-drop. SwiftUI Preview — deklaratywny podgląd w czasie rzeczywistym, gdzie interfejs jest opisywany kodem Swift. Oba narzędzia są wbudowane w Xcode, ale IB generuje XML, a SwiftUI kompiluje Swift bezpośrednio. IB obsługuje iOS 2.0+, SwiftUI — iOS 13+.
Nie, Interface Builder nie jest bezpośrednio kompatybilny ze SwiftUI. SwiftUI używa własnego deklaratywnego składni i Canvas Preview. Jednak projekty UIKit utworzone przez IB można zintegrować ze SwiftUI przez UIViewRepresentable, a widoki SwiftUI osadzać w UIKit przez UIHostingController. Umożliwia to stopniową migrację z IB na SwiftUI.
@IBDesignable — adnotacja Swift wyświetlająca niestandardowy UIView bezpośrednio w Interface Builder w czasie rzeczywistym bez uruchamiania aplikacji. @IBInspectable — adnotacja dla właściwości, dodająca je do panelu Attributes Inspector IB. Obie adnotacje przyspieszają tworzenie niestandardowych komponentów UI: wystarczy zmienić właściwość w inspektorze, a zmiana jest natychmiast widoczna na płótnie.
Auto Layout w Interface Builder ustawia ograniczenia przez menu Pin (odstępy, szerokość, wysokość) i Align (centrowanie, baseline). Każde ograniczenie to relacja matematyczna między widokami. IB wyświetla błędy czerwonymi liniami, konflikty — żółtymi ostrzeżeniami. Size Classes w IB umożliwiają ustawianie różnych ograniczeń dla różnych urządzeń i orientacji bez pisania kodu.
IBOutlet — adnotacja dla referencji do elementu UI z kodu (np. @IBOutlet weak var label: UILabel!). IBAction — adnotacja dla metody wywoływanej przy zdarzeniu (np. @IBAction func buttonTapped(_ sender: UIButton)). Połączenie jest tworzone przez Ctrl+drag z płótna IB do pliku kontrolera. Xcode automatycznie generuje kod połączenia przy puszczeniu myszy.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również