Skip to content

Programmatic UI vs Storyboards in UIKit

Overview

In iOS development with UIKit, developers can create user interfaces using Programmatic UI (writing code to define the UI) or Storyboards (a visual interface builder in Xcode). Each approach has distinct advantages and trade-offs, and choosing between them depends on project requirements, team dynamics, and personal preference. This document compares the two approaches, highlights their pros and cons, and provides a step-by-step guide to setting up a Programmatic UI in a UIKit project.

Programmatic UI vs Storyboards

What is Programmatic UI?

Programmatic UI involves creating and configuring user interface elements entirely in code, typically using Swift or Objective-C. Developers instantiate UIKit components (e.g., UIView, UIButton, UILabel) and define their properties, layouts, and behaviors programmatically.

What are Storyboards?

Storyboards are a visual tool in Xcode that allows developers to design UIs using a drag-and-drop interface. They use XML files (.storyboard) to define view hierarchies, constraints, and navigation flows, which are compiled into runtime UI components.

Comparison

AspectProgrammatic UIStoryboards
DefinitionUI defined in code (Swift/Objective-C).UI designed visually in Xcode’s Interface Builder.
SetupWritten manually, using classes like UIView, NSLayoutConstraint.Drag-and-drop interface with visual configuration.
FlexibilityHighly flexible; full control over UI logic.Limited by Interface Builder’s capabilities.
Version ControlEasy to diff and merge (text-based code).XML files are hard to diff, leading to merge conflicts.
Team CollaborationIdeal for teams using version control (e.g., Git).Prone to conflicts in collaborative environments.
PerformanceSlightly faster at runtime (no XML parsing).Minor overhead due to storyboard loading.
Learning CurveSteeper, requires understanding UIKit APIs.Easier for beginners, visual and intuitive.
ReusabilityHigh; components can be reused via code.Limited; requires duplication or custom classes.
DebuggingEasier to trace issues in code.Harder to debug runtime issues (e.g., constraint errors).

Advantages of Programmatic UI

  • Control: Full control over UI creation, enabling dynamic and complex layouts.
  • Version Control: Code-based UI integrates seamlessly with Git, making merges and diffs straightforward.
  • Reusability: Easy to create reusable UI components as classes or functions.
  • Maintainability: Code is self-documenting and easier to refactor.
  • No Runtime Surprises: Avoids Interface Builder quirks, like missing connections or outdated constraints.

Disadvantages of Programmatic UI

  • Learning Curve: Requires familiarity with UIKit APIs and Auto Layout in code.
  • More Code: Can be verbose, especially for complex UIs with many constraints.
  • Time-Intensive: Initial setup takes longer compared to Storyboards’ drag-and-drop.

Advantages of Storyboards

  • Visual Design: Intuitive for beginners, with a visual representation of the UI.
  • Rapid Prototyping: Quick to set up simple UIs and navigation flows.
  • Built-in Features: Supports segues, Interface Builder previews, and adaptive layouts.
  • Accessibility: Easy to configure accessibility properties visually.

Disadvantages of Storyboards

  • Merge Conflicts: XML-based files are difficult to manage in version control.
  • Scalability: Large storyboards become unwieldy in complex projects.
  • Limited Flexibility: Not all UIKit features are accessible via Interface Builder.
  • Maintenance: Hard to track changes or debug runtime issues (e.g., ambiguous constraints).

When to Use Each

  • Use Programmatic UI:
    • Large, collaborative projects with multiple developers.
    • Apps requiring dynamic or highly customized UIs.
    • Projects prioritizing version control and maintainability.
    • When integrating with modern frameworks or modular architectures.
  • Use Storyboards:
    • Small projects or rapid prototyping.
    • Teams with designers or less experienced developers.
    • Apps with simple, static navigation flows.
    • When leveraging Interface Builder’s visual tools (e.g., adaptive layouts).

Setting Up Programmatic UI in UIKit

This section provides a step-by-step guide to creating a UIKit project with a Programmatic UI, including setup, a sample UI, and best practices.

Step 1: Create a New UIKit Project

  1. Open Xcode (e.g., Xcode 16.3 as of May 2025).
  2. Choose File > New > Project.
  3. Select App under iOS, then:
    • Interface: Storyboard (we’ll remove it later).
    • Language: Swift.
    • Uncheck “Use Core Data” and “Include Tests” for simplicity.
  4. Name the project (e.g., “ProgrammaticUIExample”) and save it.

Step 2: Remove Storyboard Dependency

To use a fully programmatic UI, remove the storyboard and configure the app to initialize the UI in code.

  1. Delete Main.storyboard:
    • In the Project Navigator, select Main.storyboard and delete it.
  2. Update Info.plist:
    • Open Info.plist.
    • Remove the key UISceneStoryboardFile (or set it to empty).
    • Ensure UIApplicationSceneManifest is present for scene support (iOS 13+):
      xml
      <key>UIApplicationSceneManifest</key>
      <dict>
          <key>UIApplicationSupportsMultipleScenes</key>
          <false/>
          <key>UISceneConfigurations</key>
          <dict>
              <key>UIWindowSceneSessionRoleApplication</key>
              <array>
                  <dict>
                      <key>UISceneDelegateClassName</key>
                      <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
                  </dict>
              </array>
          </dict>
      </dict>
  3. Verify Target Settings:
    • In Project Settings > General > Deployment Info, ensure “Main Interface” is blank.

Step 3: Configure SceneDelegate for Programmatic UI

Create a SceneDelegate to set up the app’s UI programmatically.

swift
import UIKit

class SceneDelegate: UIResponder, UISceneDelegate {
    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = (scene as? UIWindowScene) else { return }
        
        // Initialize window
        window = UIWindow(windowScene: windowScene)
        window?.rootViewController = UINavigationController(rootViewController: ViewController())
        window?.makeKeyAndVisible()
    }
}
  • Notes:
    • The window is tied to the UIWindowScene.
    • UINavigationController is used as the root for navigation support.

Step 4: Create a Programmatic ViewController

Create a ViewController with a programmatic UI, including a label and button using Auto Layout.

swift
import UIKit

class ViewController: UIViewController {
    // UI Components
    private let welcomeLabel: UILabel = {
        let label = UILabel()
        label.text = "Welcome to Programmatic UI!"
        label.textAlignment = .center
        label.font = .systemFont(ofSize: 20, weight: .bold)
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    private let actionButton: UIButton = {
        let button = UIButton(type: .system)
        button.setTitle("Tap Me", for: .normal)
        button.titleLabel?.font = .systemFont(ofSize: 18)
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        setupActions()
    }
    
    private func setupUI() {
        view.backgroundColor = .white
        
        // Add subviews
        view.addSubview(welcomeLabel)
        view.addSubview(actionButton)
        
        // Auto Layout constraints
        NSLayoutConstraint.activate([
            welcomeLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            welcomeLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -30),
            welcomeLabel.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor, constant: 20),
            welcomeLabel.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor, constant: -20),
            
            actionButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            actionButton.topAnchor.constraint(equalTo: welcomeLabel.bottomAnchor, constant: 20)
        ])
    }
    
    private func setupActions() {
        actionButton.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
    }
    
    @objc private func buttonTapped() {
        print("Button tapped!")
    }
}
  • Notes:
    • translatesAutoresizingMaskIntoConstraints = false is set to use Auto Layout.
    • NSLayoutConstraint.activate applies multiple constraints efficiently.
    • Properties are initialized using closure syntax for clean configuration.

Step 5: Update AppDelegate (Optional)

For apps targeting iOS 12 or earlier (or non-scene-based apps), configure the UI in AppDelegate. For iOS 13+, this is typically handled in SceneDelegate.

swift
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Fallback for non-scene-based apps
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = UINavigationController(rootViewController: ViewController())
        window?.makeKeyAndVisible()
        return true
    }
}

Step 6: Run and Test

  1. Build and run the project (Cmd + R) on a simulator or device.
  2. Verify the UI displays a centered label ("Welcome to Programmatic UI!") and a button below it.
  3. Tap the button to confirm the print("Button tapped!") output in the console.

Best Practices for Programmatic UI

  • Use Auto Layout: Always set translatesAutoresizingMaskIntoConstraints = false for views using constraints.
  • Organize Code: Group UI setup, constraints, and actions into separate methods for clarity.
  • Reuse Components: Create reusable view classes or functions for common UI elements.
  • Safe Area Layout: Use view.safeAreaLayoutGuide for constraints to respect notches and home indicators:
    swift
    welcomeLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20)
  • Avoid Hardcoding: Use dynamic values (e.g., UIScreen.main.bounds) for device compatibility.
  • Test Accessibility: Add accessibility properties programmatically:
    swift
    welcomeLabel.isAccessibilityElement = true
    welcomeLabel.accessibilityLabel = "Welcome message"

Programmatic UI Example with Extension

To enhance reusability, you can use an extension like the one you previously shared:

swift
extension UIView {
    func addSubviews(_ views: UIView...) {
        for view in views {
            self.addSubview(view)
        }
    }
}

Use it in ViewController:

swift
view.addSubviews(welcomeLabel, actionButton)

Conclusion

Programmatic UI and Storyboards both have their place in UIKit development. Programmatic UI offers greater control, better version control, and reusability, making it ideal for large or dynamic projects. Storyboards are better for rapid prototyping and simpler apps. Setting up a Programmatic UI involves removing storyboard dependencies, configuring SceneDelegate or AppDelegate, and writing clean, maintainable code with Auto Layout. Start with small programmatic UIs and gradually incorporate advanced techniques like custom view classes and modular layouts.

Resources

Released under the MIT License.