ios-swiftui-patterns

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

iOS - SwiftUI Patterns

iOS - SwiftUI 模式

Modern declarative UI development for iOS, macOS, watchOS, and tvOS applications.
适用于iOS、macOS、watchOS和tvOS应用的现代声明式UI开发。

Key Concepts

核心概念

State Management Hierarchy

状态管理层级

SwiftUI provides a hierarchy of property wrappers for different state needs:
  • @State: Local view state, owned by the view
  • @Binding: Two-way connection to state owned elsewhere
  • @StateObject: Creates and owns an ObservableObject
  • @ObservedObject: References an ObservableObject owned elsewhere
  • @EnvironmentObject: Dependency injection through the view hierarchy
  • @Environment: Access to system-provided values
SwiftUI为不同的状态需求提供了一套属性包装器层级:
  • @State:视图拥有的本地视图状态
  • @Binding:与其他地方拥有的状态进行双向绑定
  • @StateObject:创建并拥有一个ObservableObject
  • @ObservedObject:引用其他地方拥有的ObservableObject
  • @EnvironmentObject:通过视图层级进行依赖注入
  • @Environment:访问系统提供的值

Observable Pattern (iOS 17+)

可观察模式(iOS 17+)

swift
@Observable
class UserModel {
    var name: String = ""
    var email: String = ""
    var isLoggedIn: Bool = false
}

struct ContentView: View {
    @State private var user = UserModel()

    var body: some View {
        UserProfileView(user: user)
    }
}
swift
@Observable
class UserModel {
    var name: String = ""
    var email: String = ""
    var isLoggedIn: Bool = false
}

struct ContentView: View {
    @State private var user = UserModel()

    var body: some View {
        UserProfileView(user: user)
    }
}

Legacy ObservableObject Pattern

传统ObservableObject模式

swift
class UserViewModel: ObservableObject {
    @Published var name: String = ""
    @Published var isLoading: Bool = false

    func fetchUser() async {
        isLoading = true
        defer { isLoading = false }
        // fetch logic
    }
}

struct UserView: View {
    @StateObject private var viewModel = UserViewModel()

    var body: some View {
        // view implementation
    }
}
swift
class UserViewModel: ObservableObject {
    @Published var name: String = ""
    @Published var isLoading: Bool = false

    func fetchUser() async {
        isLoading = true
        defer { isLoading = false }
        // fetch logic
    }
}

struct UserView: View {
    @StateObject private var viewModel = UserViewModel()

    var body: some View {
        // view implementation
    }
}

Best Practices

最佳实践

View Composition

视图组合

Break complex views into smaller, focused components:
swift
struct OrderSummaryView: View {
    let order: Order

    var body: some View {
        VStack(spacing: 16) {
            OrderHeaderView(order: order)
            OrderItemsListView(items: order.items)
            OrderTotalView(total: order.total)
        }
    }
}
将复杂视图拆分为更小、聚焦的组件:
swift
struct OrderSummaryView: View {
    let order: Order

    var body: some View {
        VStack(spacing: 16) {
            OrderHeaderView(order: order)
            OrderItemsListView(items: order.items)
            OrderTotalView(total: order.total)
        }
    }
}

Prefer Value Types

优先使用值类型

Use structs for models when possible to leverage SwiftUI's efficient diffing:
swift
struct Product: Identifiable, Equatable {
    let id: UUID
    var name: String
    var price: Decimal
    var quantity: Int
}
尽可能为模型使用结构体,以利用SwiftUI高效的差异对比机制:
swift
struct Product: Identifiable, Equatable {
    let id: UUID
    var name: String
    var price: Decimal
    var quantity: Int
}

Use ViewModifiers for Reusable Styling

使用ViewModifier实现可复用样式

swift
struct CardModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .background(Color(.systemBackground))
            .cornerRadius(12)
            .shadow(radius: 4)
    }
}

extension View {
    func cardStyle() -> some View {
        modifier(CardModifier())
    }
}
swift
struct CardModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .background(Color(.systemBackground))
            .cornerRadius(12)
            .shadow(radius: 4)
    }
}

extension View {
    func cardStyle() -> some View {
        modifier(CardModifier())
    }
}

Task Lifecycle for Async Work

异步任务的生命周期管理

swift
struct UserDetailView: View {
    let userId: String
    @State private var user: User?

    var body: some View {
        Group {
            if let user {
                UserContent(user: user)
            } else {
                ProgressView()
            }
        }
        .task {
            user = await fetchUser(id: userId)
        }
    }
}
swift
struct UserDetailView: View {
    let userId: String
    @State private var user: User?

    var body: some View {
        Group {
            if let user {
                UserContent(user: user)
            } else {
                ProgressView()
            }
        }
        .task {
            user = await fetchUser(id: userId)
        }
    }
}

Common Patterns

常见模式

Navigation with NavigationStack (iOS 16+)

使用NavigationStack进行导航(iOS 16+)

swift
struct ContentView: View {
    @State private var path = NavigationPath()

    var body: some View {
        NavigationStack(path: $path) {
            ProductListView()
                .navigationDestination(for: Product.self) { product in
                    ProductDetailView(product: product)
                }
                .navigationDestination(for: Category.self) { category in
                    CategoryView(category: category)
                }
        }
    }
}
swift
struct ContentView: View {
    @State private var path = NavigationPath()

    var body: some View {
        NavigationStack(path: $path) {
            ProductListView()
                .navigationDestination(for: Product.self) { product in
                    ProductDetailView(product: product)
                }
                .navigationDestination(for: Category.self) { category in
                    CategoryView(category: category)
                }
        }
    }
}

Sheet and Alert Presentation

弹窗与警告框的展示

swift
struct ItemView: View {
    @State private var showingDetail = false
    @State private var showingDeleteAlert = false

    var body: some View {
        Button("View Details") {
            showingDetail = true
        }
        .sheet(isPresented: $showingDetail) {
            DetailSheet()
        }
        .alert("Delete Item?", isPresented: $showingDeleteAlert) {
            Button("Delete", role: .destructive) { deleteItem() }
            Button("Cancel", role: .cancel) { }
        }
    }
}
swift
struct ItemView: View {
    @State private var showingDetail = false
    @State private var showingDeleteAlert = false

    var body: some View {
        Button("View Details") {
            showingDetail = true
        }
        .sheet(isPresented: $showingDetail) {
            DetailSheet()
        }
        .alert("Delete Item?", isPresented: $showingDeleteAlert) {
            Button("Delete", role: .destructive) { deleteItem() }
            Button("Cancel", role: .cancel) { }
        }
    }
}

List with SwiftData (iOS 17+)

结合SwiftData使用列表(iOS 17+)

swift
@Model
class Task {
    var title: String
    var isCompleted: Bool
    var createdAt: Date

    init(title: String) {
        self.title = title
        self.isCompleted = false
        self.createdAt = Date()
    }
}

struct TaskListView: View {
    @Query(sort: \Task.createdAt, order: .reverse)
    private var tasks: [Task]
    @Environment(\.modelContext) private var modelContext

    var body: some View {
        List(tasks) { task in
            TaskRowView(task: task)
        }
    }
}
swift
@Model
class Task {
    var title: String
    var isCompleted: Bool
    var createdAt: Date

    init(title: String) {
        self.title = title
        self.isCompleted = false
        self.createdAt = Date()
    }
}

struct TaskListView: View {
    @Query(sort: \Task.createdAt, order: .reverse)
    private var tasks: [Task]
    @Environment(\.modelContext) private var modelContext

    var body: some View {
        List(tasks) { task in
            TaskRowView(task: task)
        }
    }
}

Anti-Patterns

反模式

Avoid Large Monolithic Views

避免大型单体视图

Bad:
swift
struct BadView: View {
    var body: some View {
        VStack {
            // 200+ lines of nested views
        }
    }
}
Good: Extract into focused subviews.
错误示例:
swift
struct BadView: View {
    var body: some View {
        VStack {
            // 200+ lines of nested views
        }
    }
}
正确做法:拆分为聚焦的子视图。

Don't Use @ObservedObject for Owned State

不要为自有状态使用@ObservedObject

Bad:
swift
struct BadView: View {
    @ObservedObject var viewModel = ViewModel() // Re-created on every view init!
}
Good:
swift
struct GoodView: View {
    @StateObject private var viewModel = ViewModel()
}
错误示例:
swift
struct BadView: View {
    @ObservedObject var viewModel = ViewModel() // 每次视图初始化都会重新创建!
}
正确做法:
swift
struct GoodView: View {
    @StateObject private var viewModel = ViewModel()
}

Avoid Side Effects in View Body

避免在视图Body中产生副作用

Bad:
swift
var body: some View {
    let _ = print("View rendered") // Side effect!
    Text("Hello")
}
Good: Use
.task
,
.onAppear
, or
.onChange
for side effects.
错误示例:
swift
var body: some View {
    let _ = print("View rendered") // 副作用!
    Text("Hello")
}
正确做法:使用
.task
.onAppear
.onChange
处理副作用。

Don't Force Unwrap in Views

不要在视图中强制解包

Bad:
swift
Text(user!.name) // Crash risk
Good:
swift
if let user {
    Text(user.name)
}
错误示例:
swift
Text(user!.name) // 存在崩溃风险
正确做法:
swift
if let user {
    Text(user.name)
}

Related Skills

相关技能

  • ios-swift-concurrency: Async/await patterns for data loading
  • ios-uikit-architecture: When bridging UIKit and SwiftUI
  • ios-swift-concurrency:用于数据加载的Async/await模式
  • ios-uikit-architecture:桥接UIKit与SwiftUI时的相关内容