expo-ui
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseExpo @expo/ui SwiftUI Best Practices
Expo @expo/ui SwiftUI 最佳实践
Library reference for and — the iOS surface of Expo's native UI bridge. Contains 53 rules across 8 categories, prioritised by cascade impact for agents building Expo apps that render to native SwiftUI views on iOS 26 and earlier.
@expo/ui/swift-ui@expo/ui/swift-ui/modifiers@expo/ui/swift-ui@expo/ui/swift-ui/modifiersWhen to Apply
适用场景
Reference these guidelines when:
- Building a new screen with — pick the right container (Form vs List vs ScrollView), wrap in Host correctly, apply modifiers
@expo/ui/swift-ui - Migrating from React Native primitives (View, Text, TouchableOpacity) to native SwiftUI components
- Targeting iOS 26 features — Liquid Glass material, GlassEffectContainer, new sheet detent behaviours
- Reviewing code that imports from or
@expo/ui/swift-ui@expo/ui/swift-ui/modifiers - Debugging "the SwiftUI view doesn't render / is the wrong size / ignores styles" — usually a Host or modifier issue
- Composing presentation surfaces — Alert, ConfirmationDialog, BottomSheet, Popover — under HIG modality guidance
- Writing controlled inputs (TextField, Toggle, Picker, Slider) with and worklet writes
useNativeState
在以下场景中参考本指南:
- 使用构建新页面——选择合适的容器(Form/List/ScrollView)、正确用Host包裹、应用修饰符
@expo/ui/swift-ui - 从React Native基础组件(View、Text、TouchableOpacity)迁移至原生SwiftUI组件
- 开发iOS 26特性相关功能——Liquid Glass材质、GlassEffectContainer、全新sheet detent行为
- 审查导入或
@expo/ui/swift-ui的代码@expo/ui/swift-ui/modifiers - 调试「SwiftUI视图不渲染/尺寸错误/忽略样式」问题——通常是Host或修饰符的问题
- 遵循HIG模态规范构建展示层——Alert、ConfirmationDialog、BottomSheet、Popover
- 使用和worklet写入实现受控输入(TextField、Toggle、Picker、Slider)
useNativeState
When NOT to Use This Skill
不适用场景
- Android Jetpack Compose — this skill covers iOS SwiftUI only. The surface has its own conventions
@expo/ui/jetpack-compose - Universal (cross-platform) components — exposes a small set; this skill scopes to the platform-specific iOS surface
@expo/ui - Navigation routing — for stack/tab routing, use and
expo-router; this skill covers UI composition onlyexpo-router/unstable-native-tabs - Pre-iOS-17 fallbacks — most rules assume iOS 17 minimum; Liquid Glass rules require iOS 26
- Android Jetpack Compose——本技能仅覆盖iOS SwiftUI。端有独立的规范
@expo/ui/jetpack-compose - 跨平台通用组件——提供少量此类组件;本技能聚焦于iOS平台专属的实现
@expo/ui - 导航路由——栈/标签路由请使用和
expo-router;本技能仅覆盖UI组合expo-router/unstable-native-tabs - iOS 17之前版本的兼容处理——多数规则假设最低支持iOS 17;Liquid Glass相关规则要求iOS 26
Rule Categories by Priority
按优先级划分的规则类别
| Priority | Category | Impact | Prefix |
|---|---|---|---|
| 1 | Setup & Host Boundaries | CRITICAL | |
| 2 | iOS 26 HIG Composition Rules | CRITICAL | |
| 3 | Modifiers System | CRITICAL | |
| 4 | Layout Components | HIGH | |
| 5 | Input & Controls | HIGH | |
| 6 | Navigation & Overlays | HIGH | |
| 7 | Display & Feedback | MEDIUM-HIGH | |
| 8 | State & Cross-Cutting Patterns | MEDIUM | |
| 优先级 | 类别 | 影响程度 | 前缀 |
|---|---|---|---|
| 1 | 配置与Host边界 | 关键 | |
| 2 | iOS 26 HIG组合规则 | 关键 | |
| 3 | 修饰符系统 | 关键 | |
| 4 | 布局组件 | 高 | |
| 5 | 输入与控件 | 高 | |
| 6 | 导航与浮层 | 高 | |
| 7 | 展示与反馈 | 中高 | |
| 8 | 状态与跨领域模式 | 中 | |
Quick Reference
快速参考
1. Setup & Host Boundaries (CRITICAL)
1. 配置与Host边界(关键)
- — Wrap every SwiftUI subtree in a Host
host-wrap-all-swiftui-roots - — Size Host to its SwiftUI content with matchContents
host-match-contents - — Use useViewportSizeMeasurement for Form and List
host-viewport-size-for-form - — Pass explicit colorScheme when overriding the system
host-color-scheme-explicit - — Use ignoreSafeArea only for full-bleed surfaces
host-ignore-safe-area
- — 将每个SwiftUI子树包裹在Host中
host-wrap-all-swiftui-roots - — 使用matchContents让Host尺寸匹配其SwiftUI内容
host-match-contents - — 为Form和List使用useViewportSizeMeasurement
host-viewport-size-for-form - — 覆盖系统配色时传入明确的colorScheme
host-color-scheme-explicit - — 仅在全屏内容场景下使用ignoreSafeArea
host-ignore-safe-area
2. iOS 26 HIG Composition Rules (CRITICAL)
2. iOS 26 HIG组合规则(关键)
- — Group glass siblings inside GlassEffectContainer
hig-glass-effect-container - — Avoid nesting glassEffect on glass surfaces
hig-no-glass-on-glass - — Resolve a sheet before presenting another
hig-no-stacked-modals - — Don't use Popover on iPhone — use BottomSheet
hig-popover-iphone-fallback - — Include a partial detent for Liquid Glass appearance
hig-sheet-detents-partial - — ConfirmationDialog + destructive role
hig-confirmation-dialog-destructive - — Reserve tint for brand surfaces, not destructive
hig-tint-only-for-brand
- — 将玻璃效果的同级组件放入GlassEffectContainer中
hig-glass-effect-container - — 避免在玻璃表面嵌套glassEffect
hig-no-glass-on-glass - — 关闭一个弹窗后再展示另一个
hig-no-stacked-modals - — iPhone上不要使用Popover,改用BottomSheet
hig-popover-iphone-fallback - — 为Liquid Glass外观添加部分展开的detent
hig-sheet-detents-partial - — 为ConfirmationDialog设置destructive角色
hig-confirmation-dialog-destructive - — 仅在品牌专属场景使用tint,不要用于破坏性操作
hig-tint-only-for-brand
3. Modifiers System (CRITICAL)
3. 修饰符系统(关键)
- — Modifiers go through the
mod-prop-not-styleprop, not RN stylemodifiers - — Modifier order is meaningful — each wraps the previous
mod-composition-order - — Import from
mod-import-from-modifiers-subpath@expo/ui/swift-ui/modifiers - — frame proposes a size, fixedSize opts out of flex
mod-frame-vs-fixedsize - — padding for inner space, frame for outer bounds
mod-padding-vs-frame - — Presentation modifiers attach to sheet content
mod-presentation-on-sheet-content - — Use disabled modifier, don't conditionally render
mod-disabled-prop - — withAnimation wraps state-driven prop changes
mod-animation-wraps-trigger
- — 修饰符通过
mod-prop-not-style属性传递,而非RN的stylemodifiers - — 修饰符顺序有意义——每个修饰符包裹前一个
mod-composition-order - — 从
mod-import-from-modifiers-subpath导入@expo/ui/swift-ui/modifiers - — frame用于提议尺寸,fixedSize用于退出flex布局
mod-frame-vs-fixedsize - — padding用于内部间距,frame用于外部边界
mod-padding-vs-frame - — 展示类修饰符附加到sheet内容上
mod-presentation-on-sheet-content - — 使用disabled修饰符,不要条件渲染
mod-disabled-prop - — withAnimation包裹状态驱动的属性变更
mod-animation-wraps-trigger
4. Layout Components (HIGH)
4. 布局组件(高)
- — Pick stack direction by content flow
layout-hstack-vs-vstack - — LazyVStack inside ScrollView for long lists
layout-lazy-stack-for-long-lists - — Form adopts iOS grouped chrome automatically
layout-form-for-settings - — Use Section header/footer slots
layout-section-with-header-footer - — Set axes explicitly for horizontal/2D scroll
layout-scrollview-axes - — Grid for column-aligned content
layout-grid-vs-stack
- — 根据内容流向选择栈方向
layout-hstack-vs-vstack - — 长列表使用ScrollView嵌套LazyVStack
layout-lazy-stack-for-long-lists - — Form会自动适配iOS分组样式
layout-form-for-settings - — 使用Section的header/footer插槽
layout-section-with-header-footer - — 明确设置水平/二维滚动的axes
layout-scrollview-axes - — 列对齐内容使用Grid
layout-grid-vs-stack
5. Input & Controls (HIGH)
5. 输入与控件(高)
- — Set role='destructive' for delete buttons
input-button-role-for-destructive - — Use systemImage SF Symbol for button icons
input-button-systemimage - — useNativeState for TextField, not React state
input-textfield-observable-state - — SecureField for passwords, not TextField
input-securefield-for-passwords - — SyncToggle for instant flicks, Toggle for async
input-toggle-on-async - — pickerStyle modifier picks appearance
input-picker-style-via-modifier - — Constrain selectable dates with range
input-date-picker-range - — Provide min and max on Stepper
input-stepper-bounded
- — 为删除按钮设置role='destructive'
input-button-role-for-destructive - — 按钮图标使用systemImage SF Symbol
input-button-systemimage - — TextField使用useNativeState,而非React状态
input-textfield-observable-state - — 密码输入使用SecureField,而非TextField
input-securefield-for-passwords - — 即时切换用SyncToggle,异步切换用Toggle
input-toggle-on-async - — 使用pickerStyle修饰符设置外观
input-picker-style-via-modifier - — 用range约束可选日期范围
input-date-picker-range - — 为Stepper设置min和max值
input-stepper-bounded
6. Navigation & Overlays (HIGH)
6. 导航与浮层(高)
- — Alert for blocking notifications only
nav-alert-for-critical-only - — ContextMenu or SwipeActions per row, not both
nav-context-menu-vs-swipe - — Wrap BottomSheet content in Group
nav-bottom-sheet-via-group - — ShareLink for the system share sheet
nav-share-link-system - — tabViewStyle modifier picks appearance
nav-tabview-style-modifier - — DisclosureGroup for collapsible sections
nav-disclosure-group-collapsible - — Link for URLs, Button for in-app actions
nav-link-not-button-for-urls - — onPrimaryAction disambiguates tap from long-press
nav-menu-primary-action
- — Alert仅用于阻塞式通知
nav-alert-for-critical-only - — 每行使用ContextMenu或SwipeActions,不要同时使用
nav-context-menu-vs-swipe - — 将BottomSheet内容包裹在Group中
nav-bottom-sheet-via-group - — 使用ShareLink调用系统分享面板
nav-share-link-system - — 使用tabViewStyle修饰符设置外观
nav-tabview-style-modifier - — 使用DisclosureGroup实现可折叠区域
nav-disclosure-group-collapsible - — 跳转URL用Link,应用内操作用Button
nav-link-not-button-for-urls - — 使用onPrimaryAction区分点击与长按操作
nav-menu-primary-action
7. Display & Feedback (MEDIUM-HIGH)
7. 展示与反馈(中高)
- — Enable markdownEnabled for inline rich text
display-text-markdown - — Prefer systemName SF Symbols over uiImage
display-image-system-name - — ChartDataPoint arrays drive native axes
display-chart-data-points - — Provide currentValueLabel for accessibility
display-gauge-current-value-label - — Undefined value → spinner, 0 → frozen bar
display-progress-indeterminate - — systemImage for SF Symbols, icon slot for custom
display-label-icon-vs-title
- — 启用markdownEnabled支持内嵌富文本
display-text-markdown - — 优先使用systemName SF Symbols,而非uiImage
display-image-system-name - — 用ChartDataPoint数组驱动原生坐标轴
display-chart-data-points - — 提供currentValueLabel以支持无障碍访问
display-gauge-current-value-label - — 值为undefined时显示 spinner,值为0时显示冻结进度条
display-progress-indeterminate - — SF Symbols用systemImage,自定义图标用icon插槽
display-label-icon-vs-title
8. State & Cross-Cutting Patterns (MEDIUM)
8. 状态与跨领域模式(中)
- — useNativeState for every bridged input
state-use-native-state-for-fields - — Update ObservableState from worklets
state-worklet-writes - — selection or defaultSelection, not both
state-controlled-via-selection-prop - — Guard iOS 26-only features with version check
state-platform-check-pre-26 - — TextFieldRef for focus and selection
state-textfield-ref-imperative
- — 每个桥接输入都使用useNativeState
state-use-native-state-for-fields - — 从worklets更新ObservableState
state-worklet-writes - — 使用selection或defaultSelection,不要同时使用
state-controlled-via-selection-prop - — 用版本检查保护iOS 26专属特性
state-platform-check-pre-26 - — 使用TextFieldRef处理焦点与选中文本
state-textfield-ref-imperative
How to Use
使用方法
Read individual reference files for detailed explanations and code examples:
- Section definitions — Category structure and impact levels
- Rule template — Template for adding new rules
- Reference files:
references/{prefix}-{slug}.md
Each rule file contains:
- Brief explanation of why it matters in the SwiftUI bridge
- Incorrect code example anchored to a realistic domain
- Correct code example with a minimal diff from the incorrect one
- Where relevant: Alternative approach, When NOT to use, Warning callouts, authoritative reference URL
阅读单个参考文件获取详细说明和代码示例:
- 章节定义 — 类别结构与影响层级
- 规则模板 — 添加新规则的模板
- 参考文件:
references/{prefix}-{slug}.md
每个规则文件包含:
- 该规则在SwiftUI桥接中重要性的简要说明
- 基于真实场景的错误代码示例
- 与错误示例差异最小的正确代码示例
- 相关内容:替代方案、不适用场景、警告提示、权威参考链接
Gotchas
常见陷阱
See gotchas.md — append entries as failure points surface during real use.
详见gotchas.md — 在实际使用中遇到问题时可添加新条目。
Related Skills
相关技能
- For Android Jetpack Compose components, the parallel skill would target
@expo/ui/jetpack-compose - For navigation routing (stack, tabs), use directly
expo-router - For form validation libraries, see the skill
react-hook-form
- Android Jetpack Compose组件相关内容,请参考针对的平行技能
@expo/ui/jetpack-compose - 导航路由(栈、标签)请直接使用
expo-router - 表单验证库相关内容,请查看技能
react-hook-form