Communitygithub.com

SwiftUI 专家

构建整洁、高性能、无障碍的 SwiftUI 视图,具备正确的状态所有权、限定范围的无效化以及流畅的列表滚动,并能根据具体的帧时间和重渲染预算审查现有 SwiftUI 代码。

SwiftUI 专家 是什么?

该技能专注于为 AI 代理(如 Claude Code、Cursor 等)提供专业的 SwiftUI 视图构建与审查能力。其核心哲学是“状态所有权决定一切”:通过将状态放置在正确的层级(最低公共祖先),并使用最窄的包装器(如 @State、@Binding、@Observable),确保视图无效化范围最小化,避免整个屏幕因单个状态变更而重渲染。技能提供了一套结构化的工作流程:从收集输入(目标设备、数据形状、所有权映射)开始,依次进行状态设计、视图组合以限制无效化范围、遵守帧预算(120Hz 下 8.3ms)、基于状态的动画以及无障碍合规性审查。最终交付物包括可工作的 SwiftUI 代码和一份状态映射文档(说明每段状态的所有者、包装器以及读写视图),并对发现的问题按帧预算影响排序给出修复方案。该技能适用于 iOS/macOS/watchOS/tvOS 的原生 SwiftUI 开发,替代了传统的人工代码审查流程,通过约束和模式自动保证性能与可访问性基线。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/SkillMedev/skills/tree/HEAD/skills/swift-ui

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

SwiftUI 专家 是做什么的?

SwiftUI 界面的成败取决于状态所有权:将状态放在错误的位置,每次击键都会重新评估整个视图树,导致列表卡顿,动画撕裂。本技能构建并审查 SwiftUI 视图,确保无效化限定在真正读取更改数据的视图范围内,滚动时帧预算保持稳定,并且结果默认具有无障碍性,而非事后补救。

操作流程

请按此顺序操作,因为状态设计决定了后续所有环节——在视图层次结构构建完成后修复状态意味着重写层次结构。

第 1 步:收集输入

在编写任何视图代码之前进行收集。如果用户不知道,则使用默认值并将其标记为猜测。

  • 目标设备和刷新率(默认:假设一台 ProMotion 120Hz iPhone 和一台 60Hz 基线设备)。
  • 数据形态:最大列表中有多少项,数据变更频率,是否为流式数据。
  • 所有权映射:对于每段状态,哪个视图创建它,哪些视图读取它,哪些视图修改它。
  • 除基线之外的无障碍要求(默认:完整动态类型 + VoiceOver 支持)。

第 2 步:为每个值选择状态所有权

选择能够工作的最窄包装器:

  • @State - 局部、视图自有、值类型状态。也是视图创建的 @Observable 模型实例的正确所有者。
  • @Binding - 对其他地方拥有的状态的双向引用。仅当子视图修改状态时才传递绑定;若仅读取则传递普通值。
  • @Observable(Observation 框架)- 模型对象;视图仅观察它们读取的属性,因此无效化是按属性而非按对象进行的。
  • @Environment - 向下注入依赖关系到视图树中,用于服务和共享模型。

规则:状态位于每个读取或写入它的视图的最低公共祖先处——不能更低(否则视图消失时状态会重置),也不能更高(否则会扩大无效化范围)。

第 3 步:组合视图以限定无效化范围

  • 保持 body 小巧;提取子视图,使更改只无效化一行,而不是整个屏幕。超过大约 50 行或三个嵌套级别的 body 是拆分的候选。
  • 向子视图传递最少的数据——传递 item.title,而不是整个 store——这样只有受影响的视图才会重渲染。
  • 使用 @ViewBuilder 辅助方法,而不是巨大的嵌套闭包。
  • 绝不要在 body 中执行繁重的工作(格式化、排序、过滤、解码);在模型中计算完毕并将处理好的值交给视图。body 必须保持在微秒范围内,因为它在动画期间每帧都可能运行。

第 4 步:列表和帧预算

预算:120Hz 下每帧 8.3ms,60Hz 下每帧 16.7ms——包括该帧在主线程上的一切,而不仅仅是你的行。实际规则:

  • 对于超过约 50 行的列表,使用 List 或 LazyVStack,以便按需渲染行;ScrollView 中的普通 VStack 会预先构建每一行。
  • 为项提供来自数据的稳定 id(item.id),绝对不能使用数组索引或在 body 中计算的 UUID()——不稳定的 id 会破坏行标识,导致闪烁、滚动位置丢失以及行状态丢失。
  • 行的渲染成本必须恒定:不使用每行格式化日期或解码图片;缓存格式化器,使用 AsyncImage 或缓存图片管道。
  • 使用 Instruments 的 SwiftUI 模板进行验证:在滚动时观察视图-body 评估计数。当无关状态更改时,某一行类型的 body 仍被评估,这是状态所有权错误——修复第 2 步,不要绕过它进行记忆化。

第 5 步:从状态驱动动画

  • 从状态驱动动画:withAnimation { model.expanded.toggle() }。
  • 优先使用限定到特定值的 .animation(_:value:),而不是隐式全局动画,后者会动画化你无意影响的元素。
  • 对于共享元素转场使用 matchedGeometryEffect;对于插入/移除使用 transition。
  • 尊重 accessibilityReduceMotion:当该选项被设置时,用不透明度淡入淡出替换移动动画。

第 6 步:无障碍审查

  • 添加 accessibilityLabel/accessibilityValue;使用 accessibilityElement(children: .combine) 对相关元素进行分组,以便一行作为一个句子朗读,而不是五个片段。
  • 尊重动态类型 - 使用系统文本样式,没有会裁剪文本的固定框架;在最大的无障碍尺寸下进行验证。
  • 检查对比度,并为暗黑模式、大动态类型以及空/加载/错误状态添加带有示例数据的 #Preview 变体。

工作示例:状态所有权,坏的 vs 好的

坏的——每次击键整个屏幕都会无效化,因为搜索文本位于整个树都观察的模型中,并且行接收了整个 store:

@Observable final class ScreenStore {
  var searchText = ""
  var items: [Item] = []
}

struct ContactList: View {
  @State private var store = ScreenStore()
  var body: some View {
    VStack {
      TextField("搜索", text: $store.searchText)
      ForEach(store.items) { item in
        ContactRow(store: store, item: item) // 行观察一切
      }
    }
  }
}

好的——搜索文本是视图本地的,行只获取它们读取的值,并且列表是惰性的且具有稳定的 id:

struct ContactList: View {
  @State private var searchText = ""
  @Environment(ContactModel.self) private var model
  var body: some View {
    List(model.filtered(searchText)) { item in
      ContactRow(name: item.name, isOnline: item.isOnline)
    }
    .searchable(text: $searchText)
  }
}

struct ContactRow: View {
  let name: String
  let isOnline: Bool
  var body: some View {
    Label(name, systemImage: isOnline ? "circle.fill" : "circle")
  }
}

在好的版本中,输入只会无效化 List 内容闭包以及那些因过滤而成员关系发生变化的行——没有其他内容。

交付物

生成可工作的 SwiftUI 视图代码,外加一段状态映射说明,针对每段状态说明:其所有者、包装器以及哪些视图读取/写入它。对于审查,生成一个按帧预算影响排序的问题列表,每个问题附带修复方案。

禁止事项

  • 绝不在 body 评估期间修改状态——会导致循环或触发未定义行为警告。
  • 不要使用原始的 Task 在 onAppear 中执行异步工作——应使用 .task {},它会在视图消失时自动取消。
  • 不要将视图类型(Color、Image、AnyView)放入模型中;模型必须保持在没有 UI 的情况下可测试。
  • 不要“为简单起见”将所有状态提升到一个屏幕级别的 store——这恰恰是使每次击键都重绘屏幕的模式。
  • 不要在修复所有权之前使用 equatable() 或缓存来掩盖无效化错误;该错误会在下一个观察同一对象的视图中再次出现。
  • 不要在文本上使用固定框架——动态类型会裁剪它。

质量标准

当以下条件满足时,视图才算完成:

  • 每段状态都只有一个所有者,并且使用了能够工作的最窄包装器。
  • Instruments 显示行 body 仅在其自身数据更改时被评估。
  • 滚动预期的最大列表能保持基线 60Hz 设备上的帧预算。
  • 生命周期较长的状态被提升到任何可能消失的视图之上,从而能在导航中存活。
  • 键盘避让和安全区域已处理(safeAreaInset、scrollDismissesKeyboard)。
  • VoiceOver 为每个可交互元素朗读有意义的标签;布局在最大的动态类型尺寸下幸存;尊重 reduceMotion。
  • 提供了加载、空、错误、暗黑模式和大尺寸状态下的预览。

对于单个屏幕之外的整个应用性能分析,请与 mobile-perf-profiler 配合使用。

Individual skills in this repo

This repo contains 19 individual skills — each has its own dedicated page.

SkillMedev/skills

Designs REST API surfaces - resource naming, HTTP method and status-code semantics, error shapes, pagination, and filtering - and delivers an endpoint spec a consumer can build against without asking questions. Use when someone asks "how should I name this endpoint", "what status code should this return", "should this be PUT or PATCH", "how do I paginate this list", or is reviewing an API before it ships to external consumers. Do NOT use for planning breaking-change rollouts and deprecation windows - use api-versioning-strategist instead; for GraphQL type and resolver design - use graphql-schema instead; for generating client SDKs from an existing spec - use api-client-generator instead; for designing inbound webhook endpoints - use webhook-receiver-hardener instead.

SkillMedev/skills

Produce an accurate, properly timed caption track (SRT or WebVTT) from a video's audio - transcribing or aligning to the voiceover script, timing cues to speech, and enforcing line-length and reading-speed rules so captions are readable and in sync. Use when someone says "generate captions", "make subtitles from the audio", "transcribe and caption this", "export an SRT or VTT", "the captions are out of sync", or "the subtitles flash by too fast to read". Do NOT use to burn-in, style, reframe, or position an existing caption track for a platform (9:16, brand styling, safe areas) - that is social-video-formatter; do NOT use to animate text word-by-word as a motion graphic - that is kinetic-typography; do NOT use to write the spoken script in the first place - that is narration-script.

SkillMedev/skills

Turns data and charts into a decision-driving narrative structured as headline finding, trend, implication, and recommended action - with finding-led chart titles, context for every number, annotation guidance, and honest flags on any conclusion the data cannot support. Use when someone says "turn these numbers into a story", "what's the takeaway from this data", "help me present these results to leadership", or has charts but no narrative. Do NOT use for compressing a long document into a one-pager - use executive-summary instead - or for running the analysis that produces the findings - use eda-playbook instead.

SkillMedev/skills

Use when a task needs live or historical money data - "convert USD to EUR", "current/past exchange rate", "FX rate on this date / over this range", or "current price of Bitcoin/Ethereum, market cap, 24h change". Frankfurter (ECB reference rates, no key) is the FX default; CoinGecko's free keyless tier covers crypto. Do NOT use for stock quotes or equities - no keyless stock API survives verification, say so instead of guessing; do NOT use for country economic indicators like GDP or inflation series - use government-open-data instead; if the request is a vague "I need live data", route through public-data-api-picker.

SkillMedev/skills

Builds a driver-based FP&A operating model linking business inputs to P&L, balance sheet, and cash flow outputs. Use when building an annual plan, preparing investor materials, running scenario analysis, or stress-testing the business.

SkillMedev/skills

Use when a task needs live geographic lookups - "geocode this address", "what's at these coordinates" (reverse geocoding), "lat/lon for this city", "which country/state is this ZIP or postal code in", or "country facts: capital, currency, population, flag". Nominatim (OpenStreetMap) is the geocoding default; Zippopotam for postal codes; APICountries for country facts. All keyless. Do NOT use for weather at a location - use weather-climate instead; do NOT use for country-level statistics over time (GDP, population trends) - use government-open-data instead; if the request is a vague "I need live data", route through public-data-api-picker.

SkillMedev/skills

Runs the full Getting Things Done loop - capture, clarify, organize, reflect, engage - building a trusted system of context lists, a projects list with defined next actions, and a weekly review habit. Use when someone says "I'm overwhelmed and things are slipping through the cracks", "set up GTD for me", "help me do a brain dump and organize it", or "my to-do list is a mess". Do NOT use for just running the weekly review ritual itself - use weekly-review instead - or for clearing an email backlog - use inbox-zero.

SkillMedev/skills

Processes any email backlog to zero using the 4Ds - Delete, Delegate, Defer, Do - with a mass-archive strategy for the obvious, a touch-each-email-once discipline, and a keep-it-clear system of batched processing windows, ruthless unsubscribing, filters, and a minimal folder setup. Use when someone says "I have 5,000 unread emails", "help me get to inbox zero", "email is eating my whole day", or treats their inbox as a to-do list. Do NOT use for drafting the reply emails themselves or prioritization rules for an ongoing support queue - use email-triage instead - or for protecting focus time around the email windows - use deep-work-planner instead.

SkillMedev/skills

Write landing-page copy section by section - hero, social proof, features-as-benefits, objections, CTA - leading with one promise and proving it down the page. Use when someone asks "write my landing page", "improve my hero headline", "what should the homepage say", "turn these features into benefits", or is shipping a new page for a launch or campaign. Do NOT use for testing and optimizing an existing page's conversion rate - use landing-page-cro instead; for the one-line claim itself use positioning-statement; for the full message ladder use messaging-hierarchy.

SkillMedev/skills

Improves an existing landing page's conversion rate through evidence-first CRO - audit heatmaps and recordings, write structured hypotheses, prioritize with ICE scoring, and run one-variable tests to statistical significance. Use when someone asks "why isn't my landing page converting", "audit this page before we scale spend", "which A/B test should we run first", or after a CVR drop or ahead of a paid-traffic push. Do NOT use for writing a new landing page from scratch - use landing-page-copy instead. Do NOT use for auditing the ad accounts sending the traffic - use paid-acquisition-audit instead. Do NOT use for reading the results of a finished experiment - use ab-test-analyzer instead.

SkillMedev/skills

Runs structured coaching sessions using values clarification and the GROW model, ending every session with one committed action, a deadline, and an if-then plan for the likely obstacle. Use when someone says "I feel stuck in my life", "help me figure out what I want", "hold me accountable to my goals", or "coach me through this decision". Do NOT use for building a stress toolkit - use stress-management instead - or a journaling practice - use journal-framework; for a standing goal-tracking system, use goals-accountability. Coaching, not therapy: signs of clinical distress route to a licensed professional.

SkillMedev/skills

Adapts English content for a target language and region - swapping idioms, setting the right formality register, replacing cultural references, converting dates, units, and currency - and delivers the localized text with a notes column explaining each adaptation and flagging what needs a native reviewer. Use when someone asks "localize this copy for the Japanese market", "adapt our marketing copy for the Mexican market", "make this translation sound native instead of literal", or a literal translation reads foreign, stiff, or risky. Do NOT use for auditing in-product UI text quality - use ux-writing-audit instead.

SkillMedev/skills

Turns a natural-language video brief into a complete, ready-to-preview Remotion composition - extracts duration, scenes, brand colors, aspect ratio, and real copy; plans the frame budget; and writes data-driven React/TypeScript using useCurrentFrame, interpolate, spring, AbsoluteFill, and Sequence, registered in Root.tsx. Use when someone says "build a 20-second product demo video", "animate a feature walkthrough", "write the Remotion code for this marketing clip", or wants a scene-by-scene composition they can scrub in Studio. Do NOT use for rendering, iterating, or batching the finished MP4 - use remotion-render instead - or for installing and scaffolding the project - use remotion-setup instead.

SkillMedev/skills

Scaffolds a new Remotion video project wired for Claude Code Agent Skills - Node check, create-video scaffold, skills install, folder conventions, Google Fonts, and a smoke-test render. Use whenever someone wants to start making videos with Remotion and Claude, even just "make product videos with Claude.

SkillMedev/skills

Classifies incident severity (SEV1-4) using impact, scope, and urgency signals and decides who to page. Use when an alert fires or a report comes in and a severity call must be made quickly.

SkillMedev/skills

Use the Skill Me catalog from inside any conversation - discover, install, and manage Claude skills through the Skill Me MCP, and load installed skills automatically each session.

SkillMedev/skills

Write one platform-native caption from a topic and brand voice, with a front-loaded hook, native length, and a single earned CTA. Use when the user gives a topic (and optionally brand voice or a described image/video) and asks for an Instagram, LinkedIn, X/Twitter, or TikTok caption. Do NOT use when the user wants a dedicated standalone LinkedIn post - use linkedin-post-writer instead; or a multi-post X thread - use tweet-thread-builder instead; or to adapt one existing piece of content into versions for several channels - use cross-platform-reformatter instead.

SkillMedev/skills

Reframe and export a finished video for social platforms - 9:16 ↔ 16:9 cropping, burned-in captions, a platform-native first frame, loop design, and per-platform specs (aspect, length, safe areas) for TikTok, Reels, Shorts, X, and LinkedIn. Use when someone says "make this vertical", "crop to 9:16", "format for TikTok/Reels/Shorts", "add captions/subtitles", "why is my video cut off on mobile", "resize for Instagram", or "export for social". Do NOT use when the question is about the storyboard or shot order before any video exists (use video-storyboard); the animation craft of how elements move (use motion-design-principles); animated word-by-word caption styling (use kinetic-typography); palette, contrast, or lighting (use motion-color-and-light); directing the product-demo content and screen choreography (use product-demo-director); or scoring, beat-syncing, or audio (use sound-and-music-sync) - this is the craft layer that decides WHAT to format for the feed, then hands a concrete spec to remotion-compo...

SkillMedev/skills

Writes and tunes PySpark jobs - join strategy and broadcast size limits, shuffle-partition sizing, skew diagnosis and salting, UDF avoidance, caching, and output file layout - with concrete size and skew thresholds. Use when someone asks "why is my Spark job slow", "should I broadcast this join", "one task takes forever while the rest finish", "my job OOMs during a join", or is writing a new PySpark ETL job. Do NOT use for Kafka topic, consumer-group, or streaming-pipeline design - use kafka-pipelines instead; do NOT use for single-machine dataframe work that fits in memory - use pandas-expert instead.

相关技能