Skip to main content

List

Definition

List ist eine Container-View in SwiftUI, die eine scrollbare, vertikal angeordnete Collection von Views darstellt. List ist optimiert für Performance und bietet automatisches Scrolling, Sections und Swipe-Actions.

Basic Syntax

// Einfache List
List {
Text("Item 1")
Text("Item 2")
Text("Item 3")
}

// Mit Array
let items = ["Apple", "Banana", "Orange"]

List(items, id: \.self) { item in
Text(item)
}

List mit Static Content

List {
Text("First Item")
Text("Second Item")
Text("Third Item")

Image(systemName: "star")

Button("Tap Me") {
print("Tapped")
}
}

List mit Dynamic Content

struct Item: Identifiable {
let id = UUID()
let name: String
}

struct ContentView: View {
let items = [
Item(name: "Apple"),
Item(name: "Banana"),
Item(name: "Orange")
]

var body: some View {
List(items) { item in
Text(item.name)
}
}
}

List mit ForEach

// ForEach in List
List {
ForEach(items) { item in
Text(item.name)
}
}

// Mit mehreren ForEach
List {
ForEach(fruits) { fruit in
Text(fruit.name)
}

ForEach(vegetables) { veg in
Text(veg.name)
}
}

List Styles

// Automatic (Standard)
List {
Text("Item")
}
.listStyle(.automatic)

// Inset Grouped
List {
Text("Item")
}
.listStyle(.insetGrouped)

// Grouped
List {
Text("Item")
}
.listStyle(.grouped)

// Plain
List {
Text("Item")
}
.listStyle(.plain)

// Sidebar (macOS/iPad)
List {
Text("Item")
}
.listStyle(.sidebar)

Sections

// Mit Sections
List {
Section("Fruits") {
Text("Apple")
Text("Banana")
Text("Orange")
}

Section("Vegetables") {
Text("Carrot")
Text("Broccoli")
}
}

// Mit Header und Footer
List {
Section {
Text("Item 1")
Text("Item 2")
} header: {
Text("Section Header")
} footer: {
Text("Section footer text")
}
}

Custom Row Design

struct FruitRow: View {
let fruit: Fruit

var body: some View {
HStack {
Image(systemName: fruit.icon)
.foregroundColor(fruit.color)
.font(.title2)

VStack(alignment: .leading) {
Text(fruit.name)
.font(.headline)
Text(fruit.description)
.font(.caption)
.foregroundColor(.secondary)
}

Spacer()

Text("$\(fruit.price, specifier: "%.2f")")
.font(.subheadline)
.bold()
}
}
}

List(fruits) { fruit in
FruitRow(fruit: fruit)
}

Swipe Actions

List {
ForEach(items) { item in
Text(item.name)
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
delete(item)
} label: {
Label("Delete", systemImage: "trash")
}

Button {
edit(item)
} label: {
Label("Edit", systemImage: "pencil")
}
.tint(.blue)
}
.swipeActions(edge: .leading) {
Button {
favorite(item)
} label: {
Label("Favorite", systemImage: "star")
}
.tint(.yellow)
}
}
}

Delete Items

struct TodoListView: View {
@State private var todos = ["Buy milk", "Call mom", "Exercise"]

var body: some View {
List {
ForEach(todos, id: \.self) { todo in
Text(todo)
}
.onDelete { indexSet in
todos.remove(atOffsets: indexSet)
}
}
}
}

// Mit Edit Button
NavigationStack {
List {
ForEach(items) { item in
Text(item.name)
}
.onDelete { indexSet in
items.remove(atOffsets: indexSet)
}
}
.navigationTitle("Items")
.toolbar {
EditButton()
}
}

Move Items

struct ReorderListView: View {
@State private var items = ["First", "Second", "Third", "Fourth"]

var body: some View {
NavigationStack {
List {
ForEach(items, id: \.self) { item in
Text(item)
}
.onMove { source, destination in
items.move(fromOffsets: source, toOffset: destination)
}
.onDelete { indexSet in
items.remove(atOffsets: indexSet)
}
}
.navigationTitle("Reorder")
.toolbar {
EditButton()
}
}
}
}

Selection

// Single Selection
struct SingleSelectionView: View {
@State private var selectedItem: String?
let items = ["Apple", "Banana", "Orange"]

var body: some View {
NavigationStack {
List(items, id: \.self, selection: $selectedItem) { item in
Text(item)
}
.navigationTitle("Select One")
.toolbar {
EditButton()
}
}
}
}

// Multiple Selection
struct MultipleSelectionView: View {
@State private var selectedItems = Set<String>()
let items = ["Apple", "Banana", "Orange", "Grape"]

var body: some View {
NavigationStack {
List(items, id: \.self, selection: $selectedItems) { item in
Text(item)
}
.navigationTitle("Select Multiple")
.toolbar {
EditButton()
}
}
}
}

Pull to Refresh

struct RefreshableListView: View {
@State private var items = ["Item 1", "Item 2", "Item 3"]

var body: some View {
List(items, id: \.self) { item in
Text(item)
}
.refreshable {
await loadData()
}
}

func loadData() async {
// Simulate network request
try? await Task.sleep(nanoseconds: 2_000_000_000)
items.append("New Item")
}
}
struct SearchableListView: View {
@State private var searchText = ""
let allItems = ["Apple", "Banana", "Orange", "Grape", "Mango"]

var filteredItems: [String] {
if searchText.isEmpty {
return allItems
} else {
return allItems.filter { $0.localizedCaseInsensitiveContains(searchText) }
}
}

var body: some View {
NavigationStack {
List(filteredItems, id: \.self) { item in
Text(item)
}
.searchable(text: $searchText, prompt: "Search items")
.navigationTitle("Fruits")
}
}
}

Praktische Beispiele

Todo List

struct Todo: Identifiable {
let id = UUID()
var title: String
var isCompleted: Bool
}

struct TodoListView: View {
@State private var todos = [
Todo(title: "Buy groceries", isCompleted: false),
Todo(title: "Call mom", isCompleted: true),
Todo(title: "Finish project", isCompleted: false)
]
@State private var newTodo = ""

var body: some View {
NavigationStack {
VStack {
HStack {
TextField("New todo", text: $newTodo)
.textFieldStyle(.roundedBorder)

Button("Add") {
addTodo()
}
.disabled(newTodo.isEmpty)
}
.padding()

List {
ForEach($todos) { $todo in
HStack {
Button {
todo.isCompleted.toggle()
} label: {
Image(systemName: todo.isCompleted ? "checkmark.circle.fill" : "circle")
.foregroundColor(todo.isCompleted ? .green : .gray)
}

Text(todo.title)
.strikethrough(todo.isCompleted)
.foregroundColor(todo.isCompleted ? .secondary : .primary)
}
}
.onDelete { indexSet in
todos.remove(atOffsets: indexSet)
}
}
}
.navigationTitle("Todos")
.toolbar {
EditButton()
}
}
}

func addTodo() {
let todo = Todo(title: newTodo, isCompleted: false)
todos.append(todo)
newTodo = ""
}
}

Settings List

struct SettingsView: View {
@State private var notificationsEnabled = true
@State private var darkModeEnabled = false
@State private var autoSave = true

var body: some View {
NavigationStack {
List {
Section("General") {
Toggle("Notifications", isOn: $notificationsEnabled)
Toggle("Dark Mode", isOn: $darkModeEnabled)
Toggle("Auto Save", isOn: $autoSave)
}

Section("Account") {
NavigationLink("Profile") {
Text("Profile View")
}
NavigationLink("Privacy") {
Text("Privacy View")
}
}

Section("Support") {
Button("Help Center") {
openHelp()
}
Button("Contact Us") {
contactSupport()
}
}

Section {
Button("Sign Out", role: .destructive) {
signOut()
}
}
}
.navigationTitle("Settings")
}
}

func openHelp() {}
func contactSupport() {}
func signOut() {}
}

Contact List

struct Contact: Identifiable {
let id = UUID()
let name: String
let email: String
let phone: String
}

struct ContactListView: View {
let contacts = [
Contact(name: "John Doe", email: "john@example.com", phone: "555-0123"),
Contact(name: "Jane Smith", email: "jane@example.com", phone: "555-0456")
]

var body: some View {
NavigationStack {
List(contacts) { contact in
NavigationLink {
ContactDetailView(contact: contact)
} label: {
HStack {
Image(systemName: "person.circle.fill")
.font(.largeTitle)
.foregroundColor(.blue)

VStack(alignment: .leading) {
Text(contact.name)
.font(.headline)
Text(contact.email)
.font(.caption)
.foregroundColor(.secondary)
}
}
}
}
.navigationTitle("Contacts")
}
}
}

struct ContactDetailView: View {
let contact: Contact

var body: some View {
List {
Section("Information") {
LabeledContent("Name", value: contact.name)
LabeledContent("Email", value: contact.email)
LabeledContent("Phone", value: contact.phone)
}
}
.navigationTitle(contact.name)
}
}

Grouped List

struct MenuView: View {
var body: some View {
NavigationStack {
List {
Section("Main Courses") {
MenuItem(name: "Burger", price: 12.99)
MenuItem(name: "Pizza", price: 14.99)
MenuItem(name: "Pasta", price: 11.99)
}

Section("Desserts") {
MenuItem(name: "Ice Cream", price: 5.99)
MenuItem(name: "Cake", price: 6.99)
}

Section("Drinks") {
MenuItem(name: "Coffee", price: 3.99)
MenuItem(name: "Juice", price: 4.99)
}
}
.navigationTitle("Menu")
}
}
}

struct MenuItem: View {
let name: String
let price: Double

var body: some View {
HStack {
Text(name)
Spacer()
Text("$\(price, specifier: "%.2f")")
.foregroundColor(.secondary)
}
}
}

Empty State

struct ItemListView: View {
@State private var items: [Item] = []

var body: some View {
Group {
if items.isEmpty {
VStack(spacing: 20) {
Image(systemName: "tray")
.font(.system(size: 60))
.foregroundColor(.gray)

Text("No Items")
.font(.title2)
.foregroundColor(.secondary)

Button("Add First Item") {
addItem()
}
}
} else {
List(items) { item in
Text(item.name)
}
}
}
}

func addItem() {
// Add item logic
}
}

List Row Separator

// Hide all separators
List {
ForEach(items) { item in
Text(item.name)
}
}
.listRowSeparator(.hidden)

// Custom separator color
List {
ForEach(items) { item in
Text(item.name)
.listRowSeparator(.visible, edges: .all)
.listRowSeparatorTint(.blue)
}
}

List Row Background

List {
ForEach(items) { item in
Text(item.name)
.listRowBackground(
item.isImportant ? Color.yellow.opacity(0.3) : Color.clear
)
}
}

Best Practices

  1. Identifiable verwenden: Für automatische IDs
struct Item: Identifiable {
let id = UUID()
let name: String
}
  1. Lazy Loading: List ist bereits optimiert
// List lädt Rows automatisch lazy
List(largeArray) { item in
ItemRow(item: item)
}
  1. Custom Rows auslagern: Wiederverwendbare Components
struct ItemRow: View {
let item: Item
var body: some View {
// Row design
}
}

List(items) { item in
ItemRow(item: item)
}
  1. Sections für Gruppierung: Logische Gruppierung
List {
Section("Today") {
// Today's items
}
Section("Yesterday") {
// Yesterday's items
}
}

Common Use Cases

  • Todo Lists: Aufgaben-Listen mit Checkboxes
  • Settings: App-Einstellungen mit Toggles
  • Contacts: Kontaktlisten mit Navigation
  • Messages: Chat-Übersichten
  • Shopping Lists: Einkaufslisten
  • Menu Items: Restaurant-Menüs
  • Feed: Social Media Feeds
  • ForEach: Dynamic Content Iteration
  • Section: Gruppierung in Lists
  • NavigationLink: Navigation aus List
  • ScrollView: Alternative für Custom Scrolling