SwiftUI模式
Mattakushi4322026-10-26查看 561复制 200
何时激活:SwiftUI 中的 SwiftUI 视图、布局、状态管理、视图修改器、导航、列表、动画
📘如何使用这个技能
- 1复制技能内容·点击上方「Copy SKILL.md」复制全部内容,或 Download 下载到本地
- 2打开你的 AI 客户端·支持 Claude Code、Trae、Cursor、ChatGPT、DeepSeek、Kimi 等任意 AI 助手
- 3粘贴为系统提示/知识文件·Claude Code:放入 .claude/skills/ 目录;普通对话:作为首条消息发送并说「请严格按这份技能文档执行」
- 4直接提出你的需求·AI 会按技能定义的工作流、格式和约束来处理,无需再解释背景
💡 提示:技能生效后,每次对话只需一句需求,AI 会按 SKILL.md 的流程执行。
SKILL.md4610 chars
---
name: swiftui-patterns
description: "When to activate: SwiftUI views, layouts, state management, view modifiers, navigation, lists, animations in SwiftUI"
origin: zakr
metadata:
namespace: zakr/devtools-pack
version: 1.0.0
category: engineering
language: swift
platforms: [claude-code, codex, gemini-cli, cursor, aider, windsurf]
---
# SwiftUI Patterns
## State Ownership
Use the right property wrapper for the right scope.
```swift
// @State — private, view-owned mutable state
struct CounterView: View {
@State private var count = 0
var body: some View {
Button("Count: \(count)") { count += 1 }
}
}
// @Binding — child receives reference to parent state
struct ToggleRow: View {
let title: String
@Binding var isOn: Bool
var body: some View { Toggle(title, isOn: $isOn) }
}
// @StateObject — view creates and owns the ObservableObject
struct ProfileView: View {
@StateObject private var vm = ProfileViewModel()
var body: some View { Text(vm.name) }
}
// @ObservedObject — view receives (does NOT own) an ObservableObject
struct OrderRow: View {
@ObservedObject var order: Order
var body: some View { Text(order.status) }
}
// @EnvironmentObject — injected from ancestor; app-wide state
struct ThemeButton: View {
@EnvironmentObject var theme: ThemeStore
var body: some View { Button("Toggle") { theme.toggle() } }
}
```
## View Decomposition
Break large views into small, focused subviews. Use computed properties for static sub-trees.
```swift
struct ArticleView: View {
let article: Article
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
headerSection
AuthorRow(author: article.author)
Text(article.body).font(.body)
}
.padding()
}
}
private var headerSection: some View {
VStack(alignment: .leading, spacing: 8) {
Text(article.title).font(.title.bold())
Text(article.publishedAt, style: .date).foregroundStyle(.secondary)
}
}
}
```
## Navigation (NavigationStack)
Prefer typed `NavigationStack` paths over deprecated `NavigationView`.
```swift
enum Route: Hashable {
case detail(Item)
case settings
case profile(User)
}
struct RootView: View {
@State private var path: [Route] = []
var body: some View {
NavigationStack(path: $path) {
ItemListView(path: $path)
.navigationDestination(for: Route.self) { route in
switch route {
case .detail(let item): ItemDetailView(item: item)
case .settings: SettingsView()
case .profile(let user): ProfileView(user: user)
}
}
}
}
}
```
## Lists and Lazy Stacks
```swift
List(items) { item in
ItemRow(item: item)
.swipeActions(edge: .trailing) {
Button("Delete", role: .destructive) { delete(item) }
}
}
.refreshable { await viewModel.reload() }
ScrollView {
LazyVStack(spacing: 12, pinnedViews: .sectionHeaders) {
ForEach(sections) { section in
Section { ForEach(section.items) { ItemCard(item: $0) } }
header: { SectionHeader(title: section.title) }
}
}
.padding(.horizontal)
}
```
## Custom View Modifiers
```swift
struct CardStyle: ViewModifier {
var cornerRadius: CGFloat = 12
func body(content: Content) -> some View {
content
.padding()
.background(.background, in: RoundedRectangle(cornerRadius: cornerRadius))
.shadow(color: .black.opacity(0.08), radius: 8, y: 4)
}
}
extension View {
func card(cornerRadius: CGFloat = 12) -> some View {
modifier(CardStyle(cornerRadius: cornerRadius))
}
}
```
## Animations
```swift
Button("Expand") {
withAnimation(.spring(response: 0.4, dampingFraction: 0.7)) {
isExpanded.toggle()
}
}
// Hero transitions
@Namespace private var heroNamespace
// Source view
Image(item.thumbnail).matchedGeometryEffect(id: item.id, in: heroNamespace)
// Destination view
Image(item.thumbnail).matchedGeometryEffect(id: item.id, in: heroNamespace)
```
## Common Anti-Patterns
- `@State` on reference types — use `@StateObject`
- `@ObservedObject` for view-created objects — they get recreated; use `@StateObject`
- `NavigationView` — deprecated; use `NavigationStack`
- Forcing `AnyView` everywhere — keep views typed with `some View`
- One massive `body` — decompose into subviews