ClawSkills logoClawSkills

Swiftui View Refactor

重构和审查 SwiftUI 视图文件,以确保结构一致、依赖注入和 Observation 的正确使用。当被要求清理 SwiftUI 视图布局时

介绍

# SwiftUI View Refactor

_致谢:复制自 @Dimillian 的 `Dimillian/Skills` (2025-12-31)。_

## 概述

为 SwiftUI 视图应用一致的结构和依赖模式,重点关注排序、模型-视图(MV)模式、视图模型的审慎处理以及正确的 `Observation` 使用。

## 核心指南

### 1) 视图排序(从上到下)

- Environment - `private`/`public` `let` - `@State` / 其他存储属性 - 计算型 `var`(非视图) - `init` - `body` - 计算型视图构建器 / 其他视图辅助工具 - 辅助函数 / 异步函数

### 2) 优先使用 MV(模型-视图)模式

- 默认使用 MV:视图是轻量级的状态表达;模型/服务拥有业务逻辑。 - 优先使用 `@State`、`@Environment`、`@Query` 和 `task`/`onChange` 进行编排。 - 通过 `@Environment` 注入服务和共享模型;保持视图小而可组合。 - 将大型视图拆分为子视图,而不是引入视图模型。

### 3) 拆分大型 body 和视图属性

- 如果 `body` 超过一屏长度或包含多个逻辑部分,将其拆分为更小的子视图。 - 当大型计算视图属性(如 `var header: some View { ... }`)包含状态或复杂分支逻辑时,将其提取为专用的 `View` 类型。 - 将相关子视图作为同一文件中的计算视图属性是可以的;仅在结构上合理或有复用意图时才将其提取为独立的 `View` 结构体。 - 优先传递少量输入(数据、绑定、回调),而不是复用整个父视图状态。

示例(提取某个部分):

```swift var body: some View { VStack(alignment: .leading, spacing: 16) { HeaderSection(title: title, isPinned: isPinned) DetailsSection(details: details) ActionsSection(onSave: onSave, onCancel: onCancel) } } ```

示例(长 body → 更短的 body + 同一文件中的计算视图):

```swift var body: some View { List { header filters results footer } }

private var header: some View { VStack(alignment: .leading, spacing: 6) { Text(title).font(.title2) Text(subtitle).font(.subheadline) } }

private var filters: some View { ScrollView(.horizontal, showsIndicators: false) { HStack { ForEach(filterOptions, id: \.self) { option in FilterChip(option: option, isSelected: option == selectedFilter) .onTapGesture { selectedFilter = option } } } } } ```

示例(提取复杂的计算视图):

```swift private var header: some View { HeaderSection(title: title, subtitle: subtitle, status: status) }

private struct HeaderSection: View { let title: String let subtitle: String? let status: Status

var body: some View { VStack(alignment: .leading, spacing: 4) { Text(title).font(.headline) if let subtitle { Text(subtitle).font(.subheadline) } StatusBadge(status: status) } } } ```

### 4) 视图模型处理(仅当已存在时)

- 除非请求或现有代码明确要求,否则不要引入视图模型。 - 如果视图模型存在,尽可能将其设为非可选类型。 - 通过 `init` 将依赖项传递给视图,然后在视图的 `init` 中将其传递给视图模型。 - 避免 `bootstrapIfNeeded` 模式。

示例(基于 Observation):

```swift @State private var viewModel: SomeViewModel

init(dependency: Dependency) { _viewModel = State(initialValue: SomeViewModel(dependency: dependency)) } ```

### 5) Observation 的使用

- 对于 `@Observable` 引用类型,将其作为 `@State` 存储在根视图中。 - 根据需要显式向下传递 observable;除非必需,否则避免可选状态。

## 工作流

1) 重新排序视图以符合排序规则。 2) 优先使用 MV:使用 `@State`、`@Environment`、`@Query`、`task` 和 `onChange` 将轻量级编排移入视图中。 3) 如果存在视图模型,通过在 `init` 中从视图传递依赖项来初始化非可选的 `@State` 视图模型,从而替换可选视图模型。 4) 确认 Observation 的使用:根视图的 `@Observable` 视图模型使用 `@State`,无冗余包装器。 5) 保持行为完整:除非有要求,否则不要更改布局或业务逻辑。

## 备注

- 优先使用小型、显式的辅助函数,而非大型条件块。 - 将计算型视图构建器放在 `body` 下方,将非视图的计算型变量放在 `init` 上方。 - 有关 MV 优先的指导和理由,请参阅 `references/mv-patterns.md`。

更多产品