Files
2025-11-29 18:48:55 +08:00

6.9 KiB

iOS Components Catalog

Complete reference for native iOS UI components following Apple Human Interface Guidelines.

Navigation Components

Navigation Bar

Top bar for hierarchical navigation.

NavigationStack {
    List {
        Text("Item 1")
        Text("Item 2")
    }
    .navigationTitle("Title")
    .navigationBarTitleDisplayMode(.large) // or .inline
    .toolbar {
        ToolbarItem(placement: .navigationBarTrailing) {
            Button("Add") {
                // Action
            }
        }
    }
}

Guidelines:

  • Use .large for top-level views
  • Use .inline for detail views
  • Maximum 2-3 toolbar items
  • Keep actions relevant to context

Tab Bar

Bottom navigation for peer destinations.

TabView {
    HomeView()
        .tabItem {
            Label("Home", systemImage: "house")
        }

    SearchView()
        .tabItem {
            Label("Search", systemImage: "magnifyingglass")
        }
}

Guidelines:

  • 3-5 tabs maximum
  • Use SF Symbols for icons
  • One-word labels
  • Never hide or disable tabs

Toolbar

Actions for current context.

.toolbar {
    ToolbarItem(placement: .navigationBarTrailing) {
        Button("Edit") { }
    }

    ToolbarItem(placement: .bottomBar) {
        Button("Delete") { }
    }
}

Controls

Button

Primary action control.

// Prominent (primary action)
Button("Continue") { }
    .buttonStyle(.borderedProminent)

// Bordered (secondary action)
Button("Cancel") { }
    .buttonStyle(.bordered)

// Plain (tertiary action)
Button("Learn More") { }
    .buttonStyle(.plain)

Toggle (Switch)

Boolean control.

Toggle("Enable notifications", isOn: $isEnabled)
    .toggleStyle(.switch)

Slider

Continuous value selection.

Slider(value: $volume, in: 0...100)
    .accessibilityLabel("Volume")
    .accessibilityValue("\(Int(volume))%")

Picker

Selection from multiple options.

// Menu style
Picker("Size", selection: $size) {
    Text("Small").tag("S")
    Text("Medium").tag("M")
    Text("Large").tag("L")
}
.pickerStyle(.menu)

// Segmented (2-5 options)
Picker("View", selection: $viewType) {
    Text("List").tag(0)
    Text("Grid").tag(1)
}
.pickerStyle(.segmented)

TextField

Text input.

TextField("Username", text: $username)
    .textFieldStyle(.roundedBorder)
    .textContentType(.username)
    .textInputAutocapitalization(.never)
    .autocorrectionDisabled()

SecureField

Password input.

SecureField("Password", text: $password)
    .textFieldStyle(.roundedBorder)
    .textContentType(.password)

Content Views

List

Scrollable rows of content.

List {
    Section("Today") {
        ForEach(items) { item in
            NavigationLink {
                DetailView(item: item)
            } label: {
                HStack {
                    Image(systemName: item.icon)
                    Text(item.title)
                }
            }
        }
        .onDelete { indices in
            items.remove(atOffsets: indices)
        }
    }
}
.listStyle(.insetGrouped)

List Styles:

  • .plain - Edge-to-edge
  • .insetGrouped - Rounded sections (iOS default)
  • .sidebar - Navigation sidebar

ScrollView

Custom scrollable content.

ScrollView {
    VStack(spacing: 16) {
        ForEach(items) { item in
            CardView(item: item)
        }
    }
    .padding()
}

Grid

Multi-column layout.

// LazyVGrid for vertical scrolling
LazyVGrid(columns: [
    GridItem(.adaptive(minimum: 150))
]) {
    ForEach(items) { item in
        CardView(item: item)
    }
}

// LazyHGrid for horizontal scrolling
LazyHGrid(rows: [
    GridItem(.fixed(200))
]) {
    ForEach(items) { item in
        CardView(item: item)
    }
}

Presentations

Sheet (Modal)

Present content modally.

.sheet(isPresented: $showSheet) {
    NavigationStack {
        DetailView()
            .navigationTitle("Details")
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("Cancel") {
                        showSheet = false
                    }
                }
            }
    }
    .presentationDetents([.medium, .large])
}

Detents:

  • .medium - Half screen
  • .large - Full screen
  • Custom: .height(400)

Alert

Important messages.

.alert("Delete Item?", isPresented: $showAlert) {
    Button("Delete", role: .destructive) {
        deleteItem()
    }
    Button("Cancel", role: .cancel) { }
} message: {
    Text("This action cannot be undone.")
}

Confirmation Dialog

Action selection.

.confirmationDialog("Options", isPresented: $showOptions) {
    Button("Edit") { }
    Button("Share") { }
    Button("Delete", role: .destructive) { }
    Button("Cancel", role: .cancel) { }
}

Indicators

Progress View

Loading indicator.

// Indeterminate
ProgressView()

// Determinate
ProgressView(value: progress, total: 1.0)

// With label
ProgressView("Loading...") {
    // Optional current task
    Text("5 of 10 items")
}

Activity Indicator

Spinning indicator.

ProgressView()
    .progressViewStyle(.circular)
    .scaleEffect(1.5)

Content Containers

Card View

Contained content block.

struct CardView: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("Title")
                .font(.headline)

            Text("Description")
                .font(.subheadline)
                .foregroundColor(.secondary)

            Button("Action") { }
                .buttonStyle(.borderedProminent)
        }
        .padding()
        .background(Color(.systemBackground))
        .cornerRadius(12)
        .shadow(color: .black.opacity(0.1), radius: 8)
    }
}

GroupBox

Labeled group of views.

GroupBox("Settings") {
    Toggle("Notifications", isOn: $notifications)
    Toggle("Location", isOn: $location)
}

Form

Settings and input grouping.

Form {
    Section("Account") {
        TextField("Username", text: $username)
        SecureField("Password", text: $password)
    }

    Section("Preferences") {
        Toggle("Notifications", isOn: $notifications)
        Picker("Theme", selection: $theme) {
            Text("Light").tag("light")
            Text("Dark").tag("dark")
        }
    }
}

Best Practices

Navigation:

  • Use NavigationStack for hierarchical
  • Use TabView for flat (3-5 peers)
  • Never combine TabView + Toolbar in same view

Forms:

  • Group related inputs
  • Provide clear labels
  • Show validation errors inline
  • Use appropriate input types

Lists:

  • Use .insetGrouped style
  • Support swipe actions
  • Provide pull-to-refresh when relevant
  • Use sections for organization

Modals:

  • Use for focused tasks
  • Provide clear dismiss action
  • Don't nest modals
  • Use appropriate detent

For more details: https://developer.apple.com/design/human-interface-guidelines/components