visionos-widgets

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

visionOS 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 可用性

APIMinimum VersionNotes
WidgetKit on visionOSvisionOS 1.0Basic widget support
.containerBackground(for: .widget)
visionOS 1.0Removable background marking
@Environment(\.showsWidgetContainerBackground)
visionOS 1.0Background visibility check
.supportedMountingStyles()
visionOS 2.0Elevated and recessed placement
.widgetTexture(.glass / .paper)
visionOS 2.0Widget surface material
@Environment(\.levelOfDetail)
visionOS 2.0Proximity-aware layouts
.systemExtraLargePortrait
visionOS 2.0Tall portrait widget family
API最低版本说明
WidgetKit on visionOSvisionOS 1.0基础小组件支持
.containerBackground(for: .widget)
visionOS 1.0可移除背景标记
@Environment(\.showsWidgetContainerBackground)
visionOS 1.0背景可见性检查
.supportedMountingStyles()
visionOS 2.0支持悬浮和嵌入两种放置方式
.widgetTexture(.glass / .paper)
visionOS 2.0小组件表面材质
@Environment(\.levelOfDetail)
visionOS 2.0近距感知布局
.systemExtraLargePortrait
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:
.elevated
(default) sits on surfaces like tables.
.recessed
embeds into walls like a framed picture. Omit
.supportedMountingStyles()
to use elevated only.
Textures:
.glass
(default) is transparent and blends with the environment.
.paper
is opaque and poster-like, best for rich imagery.
以下示例展示了挂载样式、纹理、尺寸系列和近距感知的综合用法:
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
(默认)为透明材质,可与环境融合。
.paper
为不透明的海报样式,适合展示丰富图像。

Proximity 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
@unknown default
for forward compatibility.
系统会自动跟踪用户距离,并通过动画在不同细节层级间切换。
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 default
分支以保证向前兼容性。

Widget Families

小组件尺寸系列

FamilyDescription
.systemSmall
Compact square -- glanceable info
.systemMedium
Wide rectangle -- two-column or list preview
.systemLarge
Large square -- charts, detailed content
.systemExtraLarge
Extra-large landscape -- dashboards
.systemExtraLargePortrait
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
}())
系列描述
.systemSmall
紧凑正方形——用于快速查看信息
.systemMedium
宽矩形——双列或列表预览
.systemLarge
大正方形——展示图表、详细内容
.systemExtraLarge
横屏超大尺寸——仪表盘类内容
.systemExtraLargePortrait
竖屏超大尺寸——仅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

五大常见错误

#MistakeFix
1Missing
.containerBackground(for: .widget)
-- accented mode renders blank
Always wrap backgrounds in
.containerBackground(for: .widget) { }
2Ignoring
levelOfDetail
-- detailed views unreadable from across the room
Provide a
.simplified
layout with larger text, fewer elements
3Using
.systemExtraLargePortrait
on iOS -- build error or runtime crash
Guard with
#if os(visionOS)
or visionOS-only targets
4Hardcoding colors that clash with glass textureUse
.foregroundStyle(.primary / .secondary)
and system colors
5No
@unknown default
in
levelOfDetail
switch
Always include for forward compatibility
序号错误修复方案
1缺少
.containerBackground(for: .widget)
——强调色模式下显示空白
始终使用
.containerBackground(for: .widget) { }
包裹背景
2忽略
levelOfDetail
——远距离时详细视图无法阅读
提供
.simplified
布局,使用更大字体、更少元素
3在iOS上使用
.systemExtraLargePortrait
——编译错误或运行时崩溃
使用
#if os(visionOS)
做防护,或仅在visionOS专属目标中使用
4硬编码与玻璃纹理冲突的颜色使用
.foregroundStyle(.primary / .secondary)
和系统颜色
5
levelOfDetail
分支中缺少
@unknown default
务必添加该分支以保证向前兼容性

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
    .paper
    for widgets with rich imagery
  • Widget tested in both elevated and recessed placements (if both supported)
  • 若小组件需嵌入墙面或同时支持两种放置方式,需显式设置挂载样式
  • 含丰富图像的小组件需将纹理设置为
    .paper
  • 若支持两种挂载方式,需在两种场景下测试小组件

Proximity Awareness

近距感知

  • @Environment(\.levelOfDetail)
    provides simplified layout for distant viewers
  • .simplified
    layout uses larger text, fewer elements, high-contrast visuals
  • @unknown default
    case present in
    levelOfDetail
    switch
  • 使用
    @Environment(\.levelOfDetail)
    为远距离用户提供简化布局
  • .simplified
    布局使用更大字体、更少元素、高对比度视觉效果
  • levelOfDetail
    分支中包含
    @unknown default
    情况

Families and Layout

尺寸系列与布局

  • .systemExtraLargePortrait
    guarded with
    #if os(visionOS)
    in multiplatform targets
  • 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

背景与渲染

  • .containerBackground(for: .widget) { }
    used to mark removable backgrounds
  • Widget renders correctly in both full color and accented modes
  • showsWidgetContainerBackground
    checked if foreground colors depend on background
  • System semantic colors used for glass texture compatibility
  • 使用
    .containerBackground(for: .widget) { }
    标记可移除背景
  • 小组件在全彩和强调色模式下均可正确渲染
  • 若前景颜色依赖背景,需检查
    showsWidgetContainerBackground
    状态
  • 使用系统语义颜色以兼容玻璃纹理

References

参考资料