visionos-widgets
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesevisionOS Widgets
visionOS 小组件
Patterns for building widgets that live in physical space on visionOS. Covers mounting styles, textures, proximity-aware detail levels, spatial widget families, and rendering modes.
本内容介绍了在visionOS的物理空间中构建小组件的设计模式,涵盖挂载样式、纹理、近距感知细节层级、空间小组件系列以及渲染模式。
When This Skill Activates
本技能适用场景
Use this skill when the user:
- Asks to create or adapt a widget for visionOS
- Mentions mounting styles (elevated, recessed)
- Wants glass or paper texture on a widget
- Asks about proximity awareness or level of detail in widgets
- Mentions spatial widget families or
.systemExtraLargePortrait - Wants to control container backgrounds or rendering modes (full color vs accented)
- Is porting an existing iOS/iPadOS widget to visionOS
当用户有以下需求时,可使用本技能:
- 请求创建或适配visionOS 小组件
- 提及挂载样式(elevated、recessed)
- 希望为小组件添加玻璃或纸张纹理
- 询问小组件的近距感知或细节层级相关内容
- 提及空间小组件系列或
.systemExtraLargePortrait - 想要控制容器背景或渲染模式(全彩 vs 强调色)
- 正在将现有iOS/iPadOS小组件移植到visionOS
Decision Tree
决策树
What do you need for your visionOS widget?
|
+- Where should the widget appear?
| +- On a surface (table, shelf) -> .elevated (default)
| +- Embedded in a wall -> .recessed
| +- Both -> .supportedMountingStyles([.elevated, .recessed])
|
+- What visual treatment?
| +- Transparent, blends with environment -> .glass (default)
| +- Opaque, poster-like appearance -> .paper
|
+- How should it respond to user distance?
| +- Full detail when close -> @Environment(\.levelOfDetail) == .default
| +- Simplified when far -> @Environment(\.levelOfDetail) == .simplified
|
+- What size families?
| +- Standard -> .systemSmall, .systemMedium, .systemLarge, .systemExtraLarge
| +- Tall portrait -> .systemExtraLargePortrait (visionOS only)
|
+- How should colors render?
| +- Full color (default) -> No extra work
| +- System-tinted monochrome -> Mark backgrounds with .containerBackground(for:)What do you need for your visionOS widget?
|
+- Where should the widget appear?
| +- On a surface (table, shelf) -> .elevated (default)
| +- Embedded in a wall -> .recessed
| +- Both -> .supportedMountingStyles([.elevated, .recessed])
|
+- What visual treatment?
| +- Transparent, blends with environment -> .glass (default)
| +- Opaque, poster-like appearance -> .paper
|
+- How should it respond to user distance?
| +- Full detail when close -> @Environment(\.levelOfDetail) == .default
| +- Simplified when far -> @Environment(\.levelOfDetail) == .simplified
|
+- What size families?
| +- Standard -> .systemSmall, .systemMedium, .systemLarge, .systemExtraLarge
| +- Tall portrait -> .systemExtraLargePortrait (visionOS only)
|
+- How should colors render?
| +- Full color (default) -> No extra work
| +- System-tinted monochrome -> Mark backgrounds with .containerBackground(for:)API Availability
API 可用性
| API | Minimum Version | Notes |
|---|---|---|
| WidgetKit on visionOS | visionOS 1.0 | Basic widget support |
| visionOS 1.0 | Removable background marking |
| visionOS 1.0 | Background visibility check |
| visionOS 2.0 | Elevated and recessed placement |
| visionOS 2.0 | Widget surface material |
| visionOS 2.0 | Proximity-aware layouts |
| visionOS 2.0 | Tall portrait widget family |
| API | 最低版本 | 说明 |
|---|---|---|
| WidgetKit on visionOS | visionOS 1.0 | 基础小组件支持 |
| visionOS 1.0 | 可移除背景标记 |
| visionOS 1.0 | 背景可见性检查 |
| visionOS 2.0 | 支持悬浮和嵌入两种放置方式 |
| visionOS 2.0 | 小组件表面材质 |
| visionOS 2.0 | 近距感知布局 |
| visionOS 2.0 | 竖屏超大尺寸小组件系列 |
Complete Widget Example
完整小组件示例
This example demonstrates mounting styles, textures, families, and proximity awareness together:
swift
struct MyWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(
kind: "com.example.mywidget",
provider: Provider()
) { entry in
MyWidgetView(entry: entry)
}
.supportedFamilies([
.systemSmall, .systemMedium, .systemLarge,
.systemExtraLarge, .systemExtraLargePortrait
])
.supportedMountingStyles([.elevated, .recessed])
.widgetTexture(.glass) // .glass is default, .paper for opaque
}
}Mounting styles: (default) sits on surfaces like tables. embeds into walls like a framed picture. Omit to use elevated only.
.elevated.recessed.supportedMountingStyles()Textures: (default) is transparent and blends with the environment. is opaque and poster-like, best for rich imagery.
.glass.paper以下示例展示了挂载样式、纹理、尺寸系列和近距感知的综合用法:
swift
struct MyWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(
kind: "com.example.mywidget",
provider: Provider()
) { entry in
MyWidgetView(entry: entry)
}
.supportedFamilies([
.systemSmall, .systemMedium, .systemLarge,
.systemExtraLarge, .systemExtraLargePortrait
])
.supportedMountingStyles([.elevated, .recessed])
.widgetTexture(.glass) // .glass 是默认值,.paper 为不透明材质
}
}挂载样式:(默认)用于放置在桌面、架子等平面上。用于嵌入墙面,类似挂画效果。省略则仅使用悬浮样式。
.elevated.recessed.supportedMountingStyles()纹理:(默认)为透明材质,可与环境融合。为不透明的海报样式,适合展示丰富图像。
.glass.paperProximity Awareness (Level of Detail)
近距感知(细节层级)
The system tracks user distance and transitions between detail levels automatically with animation.
swift
struct MyWidgetView: View {
let entry: Provider.Entry
@Environment(\.levelOfDetail) private var levelOfDetail
var body: some View {
switch levelOfDetail {
case .default:
VStack(alignment: .leading, spacing: 8) {
Text(entry.title).font(.headline)
Text(entry.subtitle).font(.subheadline).foregroundStyle(.secondary)
DetailChart(data: entry.chartData)
}
.padding()
case .simplified:
VStack(spacing: 4) {
Image(systemName: entry.iconName).font(.largeTitle)
Text(entry.title).font(.headline)
}
.padding()
@unknown default:
Text(entry.title).padding()
}
}
}Always handle for forward compatibility.
@unknown default系统会自动跟踪用户距离,并通过动画在不同细节层级间切换。
swift
struct MyWidgetView: View {
let entry: Provider.Entry
@Environment(\.levelOfDetail) private var levelOfDetail
var body: some View {
switch levelOfDetail {
case .default:
VStack(alignment: .leading, spacing: 8) {
Text(entry.title).font(.headline)
Text(entry.subtitle).font(.subheadline).foregroundStyle(.secondary)
DetailChart(data: entry.chartData)
}
.padding()
case .simplified:
VStack(spacing: 4) {
Image(systemName: entry.iconName).font(.largeTitle)
Text(entry.title).font(.headline)
}
.padding()
@unknown default:
Text(entry.title).padding()
}
}
}务必处理分支以保证向前兼容性。
@unknown defaultWidget Families
小组件尺寸系列
| Family | Description |
|---|---|
| Compact square -- glanceable info |
| Wide rectangle -- two-column or list preview |
| Large square -- charts, detailed content |
| Extra-large landscape -- dashboards |
| Extra-large portrait -- visionOS only |
Guard the visionOS-only family in multiplatform targets:
swift
.supportedFamilies({
var families: [WidgetFamily] = [.systemSmall, .systemMedium, .systemLarge]
#if os(visionOS)
families.append(.systemExtraLargePortrait)
#endif
return families
}())| 系列 | 描述 |
|---|---|
| 紧凑正方形——用于快速查看信息 |
| 宽矩形——双列或列表预览 |
| 大正方形——展示图表、详细内容 |
| 横屏超大尺寸——仪表盘类内容 |
| 竖屏超大尺寸——仅visionOS支持 |
在多平台项目中需对visionOS专属尺寸系列做防护处理:
swift
.supportedFamilies({
var families: [WidgetFamily] = [.systemSmall, .systemMedium, .systemLarge]
#if os(visionOS)
families.append(.systemExtraLargePortrait)
#endif
return families
}())Container Backgrounds and Rendering Modes
容器背景与渲染模式
In accented rendering mode, the system removes backgrounds and applies a tint color. Mark removable backgrounds so the widget renders correctly in both modes.
swift
struct MyWidgetView: View {
let entry: Provider.Entry
@Environment(\.showsWidgetContainerBackground) var showsBackground
var body: some View {
VStack {
Image(systemName: "star.fill").font(.largeTitle)
Text(entry.title)
.font(.headline)
.foregroundStyle(showsBackground ? .white : .primary)
}
.padding()
.containerBackground(for: .widget) {
LinearGradient(
colors: [.blue, .purple],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
}
}
}- Full color (default): All colors render intact.
- Accented: Container background is removed; system applies a monochrome tint.
在强调色渲染模式下,系统会移除背景并应用色调颜色。需标记可移除背景,确保小组件在两种模式下都能正确显示。
swift
struct MyWidgetView: View {
let entry: Provider.Entry
@Environment(\.showsWidgetContainerBackground) var showsBackground
var body: some View {
VStack {
Image(systemName: "star.fill").font(.largeTitle)
Text(entry.title)
.font(.headline)
.foregroundStyle(showsBackground ? .white : .primary)
}
.padding()
.containerBackground(for: .widget) {
LinearGradient(
colors: [.blue, .purple],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
}
}
}- 全彩模式(默认):所有颜色正常显示。
- 强调色模式:容器背景被移除,系统应用单色色调。
Previewing visionOS Widgets
预览visionOS小组件
swift
#Preview("Close Up", as: .systemSmall) {
MyWidget()
} timelineProvider: {
Provider()
}
#Preview("Extra Large Portrait", as: .systemExtraLargePortrait) {
MyWidget()
} timelineProvider: {
Provider()
}swift
#Preview("Close Up", as: .systemSmall) {
MyWidget()
} timelineProvider: {
Provider()
}
#Preview("Extra Large Portrait", as: .systemExtraLargePortrait) {
MyWidget()
} timelineProvider: {
Provider()
}Top 5 Mistakes
五大常见错误
| # | Mistake | Fix |
|---|---|---|
| 1 | Missing | Always wrap backgrounds in |
| 2 | Ignoring | Provide a |
| 3 | Using | Guard with |
| 4 | Hardcoding colors that clash with glass texture | Use |
| 5 | No | Always include for forward compatibility |
| 序号 | 错误 | 修复方案 |
|---|---|---|
| 1 | 缺少 | 始终使用 |
| 2 | 忽略 | 提供 |
| 3 | 在iOS上使用 | 使用 |
| 4 | 硬编码与玻璃纹理冲突的颜色 | 使用 |
| 5 | | 务必添加该分支以保证向前兼容性 |
Anti-Patterns
反模式示例
swift
// ❌ No container background — accented mode shows nothing
struct BadWidgetView: View {
var body: some View {
ZStack {
Color.blue // Not marked as removable
Text("Hello")
}
}
}
// ✅ Background marked as removable
struct GoodWidgetView: View {
var body: some View {
Text("Hello")
.containerBackground(for: .widget) { Color.blue }
}
}swift
// ❌ Same complex layout at all distances
struct BadProximityView: View {
var body: some View {
VStack {
Text(entry.title).font(.caption2) // Unreadable far away
DetailChart(data: entry.data)
}
}
}
// ✅ Simplified layout when far away
struct GoodProximityView: View {
@Environment(\.levelOfDetail) private var levelOfDetail
var body: some View {
switch levelOfDetail {
case .default: DetailedLayout(entry: entry)
case .simplified: SimplifiedLayout(entry: entry)
@unknown default: SimplifiedLayout(entry: entry)
}
}
}swift
// ❌ 无容器背景——强调色模式下无内容显示
struct BadWidgetView: View {
var body: some View {
ZStack {
Color.blue // 未标记为可移除
Text("Hello")
}
}
}
// ✅ 背景标记为可移除
struct GoodWidgetView: View {
var body: some View {
Text("Hello")
.containerBackground(for: .widget) { Color.blue }
}
}swift
// ❌ 所有距离下使用相同复杂布局
struct BadProximityView: View {
var body: some View {
VStack {
Text(entry.title).font(.caption2) // 远距离无法阅读
DetailChart(data: entry.data)
}
}
}
// ✅ 远距离时使用简化布局
struct GoodProximityView: View {
@Environment(\.levelOfDetail) private var levelOfDetail
var body: some View {
switch levelOfDetail {
case .default: DetailedLayout(entry: entry)
case .simplified: SimplifiedLayout(entry: entry)
@unknown default: SimplifiedLayout(entry: entry)
}
}
}Review Checklist
检查清单
Mounting and Texture
挂载与纹理
- Mounting style explicitly set if widget should appear recessed or support both
- Texture set to for widgets with rich imagery
.paper - Widget tested in both elevated and recessed placements (if both supported)
- 若小组件需嵌入墙面或同时支持两种放置方式,需显式设置挂载样式
- 含丰富图像的小组件需将纹理设置为
.paper - 若支持两种挂载方式,需在两种场景下测试小组件
Proximity Awareness
近距感知
- provides simplified layout for distant viewers
@Environment(\.levelOfDetail) - layout uses larger text, fewer elements, high-contrast visuals
.simplified - case present in
@unknown defaultswitchlevelOfDetail
- 使用为远距离用户提供简化布局
@Environment(\.levelOfDetail) - 布局使用更大字体、更少元素、高对比度视觉效果
.simplified - 分支中包含
levelOfDetail情况@unknown default
Families and Layout
尺寸系列与布局
- guarded with
.systemExtraLargePortraitin multiplatform targets#if os(visionOS) - Widget content adapts to each supported family size
- Layout tested in all declared family sizes via Xcode previews
- 在多平台项目中,需用
.systemExtraLargePortrait做防护#if os(visionOS) - 小组件内容适配所有支持的尺寸系列
- 通过Xcode预览在所有声明的尺寸系列中测试布局
Backgrounds and Rendering
背景与渲染
- used to mark removable backgrounds
.containerBackground(for: .widget) { } - Widget renders correctly in both full color and accented modes
- checked if foreground colors depend on background
showsWidgetContainerBackground - System semantic colors used for glass texture compatibility
- 使用标记可移除背景
.containerBackground(for: .widget) { } - 小组件在全彩和强调色模式下均可正确渲染
- 若前景颜色依赖背景,需检查状态
showsWidgetContainerBackground - 使用系统语义颜色以兼容玻璃纹理