List

A styled list component inspired by Family.co, perfect for settings screens, wallet lists, and navigation patterns. Supports icons, accessories, toggles, and interactive items.

Import

import PrettyUI

Basic Usage

PList {
    PListItem("Settings")
    PListItem("Profile")
    PListItem("Notifications")
}

With Header

PList("Account") {
    PListItem("Email", subtitle: "john@example.com")
    PListItem("Password")
        .accessory(.chevron)
}

List Styles

Three styles are available:

// Inset Grouped (default) - rounded corners with margins
PList("Header", style: .insetGrouped) {
    // items...
}

// Grouped - rounded corners, full width
PList("Header", style: .grouped) {
    // items...
}

// Plain - no card styling
PList("Header", style: .plain) {
    // items...
}

List Items

Basic Item

PListItem("Settings")

With Subtitle

PListItem("Email", subtitle: "john@example.com")

With Icon

PListItem("Profile", subtitle: "John Doe", icon: "person.fill")
PListItem("Notifications", icon: "bell.fill")

Custom Icon Color

PListItem("Delete", icon: "trash.fill", iconColor: .red)

Accessories

Add trailing accessories to indicate actions or state:

// Navigation chevron
PListItem("Settings")
    .accessory(.chevron)

// Selection checkmark
PListItem("Selected Option")
    .accessory(.checkmark)

// Custom icon
PListItem("Sync Status")
    .accessory(.icon("checkmark.icloud"))

// Badge
PListItem("Inbox")
    .accessory(.badge("3"))
AccessoryDescription
.chevronRight arrow for navigation
.checkmarkCheckmark for selection
.icon(String)Custom SF Symbol
.badge(String)Text badge (e.g., counts)
.noneNo accessory

Interactive Items

Add tap actions to list items:

PListItem("Profile", icon: "person.fill")
    .accessory(.chevron)
    .onTap {
        navigateToProfile()
    }

Divider Styles

Control dividers between items:

// Inset (default) - aligned with content
PListItem("Item").divider(.inset)

// Full width
PListItem("Item").divider(.full)

// No divider (use for last item)
PListItem("Last Item").hideDivider()

Toggle Items

Use PListToggleItem for switch controls:

@State private var pushEnabled = true
@State private var emailEnabled = false

PList("Notifications") {
    PListToggleItem("Push Notifications", icon: "bell.fill", isOn: $pushEnabled)
    PListToggleItem("Email Notifications", icon: "envelope.fill", isOn: $emailEnabled)
        .hideDivider()
}

Destructive Items

Mark items as destructive:

PListItem("Delete Account", icon: "trash.fill", iconColor: .red)
    .destructive()
    .onTap { showDeleteConfirmation() }

Custom Leading Content

Use custom views for leading content:

PListItem("John Doe", subtitle: "Online") {
    PAvatar(name: "John Doe")
        .size(.sm)
        .status(.online)
}
.accessory(.chevron)

List Sections

Group items with headers and footers:

PListSection("Account", footer: "Manage your account settings.") {
    PListItem("Profile", icon: "person.fill")
    PListItem("Security", icon: "lock.fill")
}

PListSection("Support") {
    PListItem("Help Center", icon: "questionmark.circle")
    PListItem("Contact Us", icon: "envelope")
}

Add explanatory text below the list:

PList(footer: "This action cannot be undone.") {
    PListItem("Delete Account", icon: "trash.fill", iconColor: .red)
        .destructive()
        .hideDivider()
}

Real-World Examples

Settings Screen

ScrollView {
    VStack(spacing: 24) {
        PList("Account") {
            PListItem("Profile", subtitle: "John Doe", icon: "person.fill")
                .accessory(.chevron)
                .onTap { }
            
            PListItem("Email", subtitle: "john@example.com", icon: "envelope.fill")
                .accessory(.chevron)
                .onTap { }
            
            PListItem("Password", icon: "lock.fill")
                .accessory(.chevron)
                .hideDivider()
                .onTap { }
        }
        
        PList("Preferences") {
            PListToggleItem("Push Notifications", icon: "bell.fill", isOn: $pushEnabled)
            PListToggleItem("Dark Mode", icon: "moon.fill", isOn: $darkMode)
            PListToggleItem("Sounds", icon: "speaker.wave.2.fill", isOn: $soundEnabled)
                .hideDivider()
        }
        
        PList(footer: "Deleting your account will remove all data permanently.") {
            PListItem("Delete Account", icon: "trash.fill", iconColor: .red)
                .destructive()
                .hideDivider()
                .onTap { showDeleteAlert = true }
        }
    }
    .padding()
}

Wallet List

PList("Wallets") {
    PListItem("Main Wallet", subtitle: "0x1234...5678") {
        PAvatar(name: "Main")
            .size(.sm)
            .iconBadge("checkmark.circle.fill", backgroundColor: .green)
    }
    .accessory(.badge("Primary"))
    .onTap { }
    
    PListItem("Trading Wallet", subtitle: "0xabcd...efgh") {
        PAvatar(name: "Trading")
            .size(.sm)
    }
    .accessory(.chevron)
    .onTap { }
    
    PListItem("Add Wallet", icon: "plus.circle.fill")
        .hideDivider()
        .onTap { showAddWallet = true }
}

Contact List

PList("Contacts") {
    ForEach(contacts) { contact in
        PListItem(contact.name, subtitle: contact.status) {
            PAvatar(name: contact.name)
                .size(.sm)
                .status(contact.isOnline ? .online : .offline)
        }
        .accessory(.chevron)
        .onTap { selectContact(contact) }
    }
}

Selection List

PList("Choose Theme") {
    ForEach(Theme.allCases) { theme in
        PListItem(theme.name, icon: theme.icon)
            .accessory(selectedTheme == theme ? .checkmark : .none)
            .onTap { selectedTheme = theme }
    }
}

API Reference

PList

PList(
    _ header: String? = nil,
    footer: String? = nil,
    style: PListStyle = .insetGrouped,
    @ViewBuilder content: () -> Content
)

PListItem

// Simple
PListItem(_ title: String, subtitle: String?, icon: String?, iconColor: Color?)

// With custom leading
PListItem(_ title: String, subtitle: String?, ..., @ViewBuilder leading: () -> Leading)

Modifiers:

ModifierTypeDescription
.accessory(_:)PListAccessoryTrailing accessory
.divider(_:)PListDividerStyleDivider style
.hideDivider()Remove divider
.destructive(_:)BoolMark as destructive
.onTap(_:)() -> VoidTap action

PListToggleItem

PListToggleItem(
    _ title: String,
    subtitle: String? = nil,
    icon: String? = nil,
    iconColor: Color? = nil,
    isOn: Binding<Bool>
)

PListSection

PListSection(
    _ header: String? = nil,
    footer: String? = nil,
    @ViewBuilder content: () -> Content
)

Enums

PListStyle

  • .insetGrouped — Rounded with margins (default)
  • .grouped — Rounded, full width
  • .plain — No card styling

PListAccessory

  • .chevron — Navigation arrow
  • .checkmark — Selection indicator
  • .icon(String) — Custom SF Symbol
  • .badge(String) — Text badge
  • .none — No accessory

PListDividerStyle

  • .inset — Aligned with content (default)
  • .full — Full width
  • .none — No divider