mcpField
U
ui-patterns

ui-patterns

github.com/patrickserrano开发1366 次安装42 周前更新
代码仓库

关于 ui-patterns

面向 SwiftUI 开发者的界面模式技能,指导在现有或新项目中组织视图、状态和导航。强调小型组合视图、SwiftUI 原生数据流,以及由视图管理的 Sheet 操作。

主要功能

  1. 01规划视图状态与归属
  2. 02组合小型、职责明确的视图
  3. 03使用 SwiftUI 状态与环境注入
  4. 04处理异步加载和错误状态
  5. 05采用项目现有代码风格

适用场景

  1. 01为新功能构建列表、详情或编辑界面
  2. 02为新项目搭建标签页与导航
  3. 03统一 Sheet 和状态管理模式

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/patrickserrano/skills --skill ui-patterns

安装后在 Claude Code 中输入/ui-patterns确认已加载。

详细介绍

SwiftUI UI 模式

快速入门

现有项目
  1. 确定功能和主要交互模式(列表、详情、编辑器、设置、标签页)
  2. 使用 rg "TabView\(" 或类似命令,在仓库中查找相近示例
  3. 遵循本地约定:优先使用 SwiftUI 原生状态,并将状态保留在本地
  4. 使用小型、专注的子视图构建,并采用 SwiftUI 原生数据流
新项目脚手架
  1. 配置 TabView + NavigationStack + sheets
  2. 添加最简的 AppTab 枚举和 RouterPath
  3. 随着添加新屏幕,扩展路由和 sheet 枚举

通用规则

  • 使用现代 SwiftUI 状态(@State、@Binding、@Observable、@Environment)
  • 避免不必要的视图模型——优先采用 MV(Model-View),而非 MVVM
  • 优先使用组合;让视图保持精简且专注
  • 使用 .task 配合 async/await,并明确处理加载和错误状态
  • 遵循项目的格式化工具和风格指南
Sheet 最佳实践
  • 当状态表示选中的模型时,优先使用 .sheet(item:),而非 .sheet(isPresented:)
  • 避免在 sheet 主体中使用 if let
  • Sheet 应负责自身的操作,并在内部调用 dismiss()

新建 SwiftUI 视图的工作流

  1. 定义视图的状态及其所有权位置
  2. 确定通过 @Environment 注入的依赖项
  3. 勾勒视图层级,并将重复部分提取为子视图
  4. 使用 .task 实现异步加载;必要时使用明确的状态枚举
  5. 为交互式 UI 添加无障碍标签或标识符
  6. 通过构建进行验证,并在需要时更新使用位置

MV 模式(优先于 MVVM)

SwiftUI 视图应是轻量的状态表达式。除非确有必要,否则避免使用 ViewModel。

struct FeedView: View {
    @Environment(APIClient.self) private var client

    enum ViewState {
        case loading
        case error(String)
        case loaded([Post])
    }

    @State private var viewState: ViewState = .loading

    var body: some View {
        NavigationStack {
            List {
                switch viewState {
                case .loading:
                    ProgressView("Loading...")
                case .error(let message):
                    ErrorView(message: message, retry: { await loadFeed() })
                case .loaded(let posts):
                    ForEach(posts) { post in
                        PostRow(post: post)
                    }
                }
            }
            .task { await loadFeed() }
        }
    }

    private func loadFeed() async {
        do {
            let posts = try await client.getFeed()
            viewState = .loaded(posts)
        } catch {
            viewState = .error(error.localizedDescription)
        }
    }
}

Sheet 模式

由项目驱动的 sheet(推荐)
@State private var selectedItem: Item?

.sheet(item: $selectedItem) { item in
    EditItemSheet(item: item)
}
由 Sheet 负责自身操作
struct EditItemSheet: View {
    @Environment(\.dismiss) private var dismiss
    @Environment(Store.self) private var store

    let item: Item
    @State private var isSaving = false

    var body: some View {
        VStack {
            Button(isSaving ? "Saving..." : "Save") {
                Task { await save() }
            }
        }
    }

    private func save() async {
        isSaving = true
        await store.save(item)
        dismiss()
    }
}

应用级环境设置

@main
struct MyApp: App {
    @State var client: APIClient = .init()
    @State var router: AppRouter = .init()

    var body: some Scene {
        WindowGroup {
            TabView(selection: $router.selectedTab) {
                ForEach(AppTab.allCases) { tab in
                    tab.rootView
                        .tag(tab)
                }
            }
            .environment(client)
            .environment(router)
        }
    }
}

状态管理指南

包装器使用场景
@State本地、临时的视图状态
@Binding来自父视图的双向数据流
@Observable跨视图共享状态(iOS 17+)
@Environment依赖注入、应用级关注点
@Query在视图中直接执行 SwiftData 查询

Task 和 onChange 模式

// React to state changes
.task(id: searchText) {
    guard !searchText.isEmpty else { return }
    await search(query: searchText)
}

// Respond to state transitions
.onChange(of: isActive, initial: false) {
    guard isActive else { return }
    Task { await refresh() }
}

为什么不使用 MVVM?

SwiftUI 的设计初衷并不包括 ViewModel:

  • 视图是结构体,轻量且可随时丢弃
  • @State、@Environment、@Observable 可满足所有数据流需求
  • ViewModel 会增加复杂性、间接层和认知负担
  • SwiftData 的 @Query 可直接在视图中使用

建议改为:

  • 让视图成为状态的纯粹表达
  • 将业务逻辑放入通过 @Environment 注入的服务/模型中
  • 测试服务和模型,而非视图
  • 使用 SwiftUI Previews 进行视觉回归测试

评论

支持 Markdown