关于 visionos-widgets
面向 visionOS 空间环境中 Widget 开发的指南,介绍安装方式、纹理、距离感知布局、Widget 系列、背景和渲染模式。
主要功能
- 01凸起与嵌入式安装
- 02玻璃与纸张纹理
- 03距离感知的细节级别
- 04空间 Widget 系列
- 05背景与渲染模式处理
适用场景
- 01将现有 iOS Widget 适配到 visionOS
- 02为桌面或墙面选择安装方式
- 03在远距离简化 Widget 内容
- 04支持全彩与强调色渲染
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ rshankras/ claude-code-apple-skills --skill visionos-widgets安装后在 Claude Code 中输入/visionos-widgets确认已加载。
详细介绍
visionOS 小组件
介绍在 visionOS 空间中呈现的小组件构建模式,涵盖安装样式、纹理、支持距离感知的详细程度、空间小组件系列和渲染模式。
此技能的触发条件
在以下情况下使用此技能:
- 用户要求创建或改造 visionOS 小组件
- 提到 安装样式(凸起式、嵌入式)
- 希望为小组件添加 玻璃或纸张纹理
- 询问小组件中的 距离感知 或 详细程度
- 提到 空间小组件系列 或
.systemExtraLargePortrait - 希望控制 容器背景 或 渲染模式(全彩或强调色)
- 将现有 iOS/iPadOS 小组件移植到 visionOS
决策树
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 可用性
| API | 最低版本 | 说明 |
|---|---|---|
| visionOS 上的 WidgetKit | visionOS 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 | 高纵向小组件系列 |
完整小组件示例
此示例展示了如何结合使用安装样式、纹理、小组件系列和距离感知:
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
}
}
安装样式:.elevated(默认)会放置在桌面等表面上。.recessed 会嵌入墙面,如同装裱好的画作。省略 .supportedMountingStyles() 即仅使用凸起式。嵌入式仅适用于垂直表面;放置在水平表面时始终采用凸起式。在水平表面上,系统还会轻微倾斜小组件,使其朝向用户;请针对这个角度进行设计,而不是试图抵消它。
纹理:.glass(默认)与环境融为一体;.paper 不透明,最适合丰富的图像内容。
空间行为(WWDC25)
小组件是固定在实体表面上的房间装饰物——它们会在不同会话、房间切换和电源循环期间保留,并且一个房间中可以同时存在多个实例。
- 用户调整大小:用户可以通过角落控件将小组件缩放到模板尺寸的 75% 至 125%——布局必须适应整个范围。
- 边框:用户可以从 五种边框宽度(从细到粗)中选择,且宽度独立于模板尺寸。嵌入式样式会固定边框宽度。
- 边框着色:小组件边框始终会应用用户的颜色色调,无法选择退出。即使背景选择不应用色调,前景内容也必须在全部 7 种浅色和 7 种深色系统调色板下清晰可辨。
- 资源:小组件以真实世界的尺寸渲染——请提供高分辨率资源,确保图像在近距离观看时依然清晰。
距离感知(详细程度)
系统会根据用户距离,在 .default(近)和 .simplified(远)之间自动切换,并带有动画效果——可通过降低内容密度、放大关键信息来简化布局。
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,以确保向前兼容。
小组件系列
| 系列 | 说明 |
|---|---|
.systemSmall | 紧凑正方形——用于一目了然的信息 |
.systemMedium | 宽矩形——用于双列布局或列表预览 |
.systemLarge | 大型正方形——用于图表和详细内容 |
.systemExtraLarge | 超大型横向布局——用于仪表板 |
.systemExtraLargePortrait | 超大型纵向布局——仅限 visionOS;用于墙面装饰的“醒目”小组件 |
在多平台目标中,为仅限 visionOS 的小组件系列添加保护条件:
.supportedFamilies({
var families: [WidgetFamily] = [.systemSmall, .systemMedium, .systemLarge]
#if os(visionOS)
families.append(.systemExtraLargePortrait)
#endif
return families
}())
容器背景和渲染模式
在强调色渲染模式下,系统会移除背景并应用色调。请标记可移除的背景,确保小组件在两种模式下都能正确渲染。
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
)
}
}
}
- 全彩(默认):所有颜色均按原样渲染。
- 强调色:容器背景会被移除;系统会应用单色调。
预览 visionOS 小组件
#Preview("Close Up", as: .systemSmall) {
MyWidget()
} timelineProvider: {
Provider()
}
#Preview("Extra Large Portrait", as: .systemExtraLargePortrait) {
MyWidget()
} timelineProvider: {
Provider()
}
五大常见错误
| # | 错误 | 修复方法 |
|---|---|---|
| 1 | 缺少 .containerBackground(for: .widget)——强调模式下会显示空白 | 始终使用 .containerBackground(for: .widget) { } 包裹背景 |
| 2 | 忽略 levelOfDetail——从房间另一端看时,详细视图难以阅读 | 提供 .simplified 布局,使用更大的文字和更少的元素 |
| 3 | 在 iOS 上使用 .systemExtraLargePortrait——导致构建错误或运行时崩溃 | 使用 #if os(visionOS) 或仅面向 visionOS 的目标进行条件限制 |
| 4 | 硬编码与玻璃质感冲突的颜色 | 使用 .foregroundStyle(.primary / .secondary) 和系统颜色 |
| 5 | levelOfDetail 的 switch 中没有 @unknown default | 始终包含此项,以确保向前兼容 |
反模式
// ❌ 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 }
}
}
// ❌ 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)
}
}
}
审查清单
安装方式和质感
- 如果小组件应呈现凹陷效果或支持两种效果,需明确设置安装样式
- 对于包含丰富图像的小组件,将质感设为
.paper - 在凸起和凹陷位置均测试小组件(如果两者都支持)
- 布局在用户将尺寸从模板大小的 75% 调整到 125% 后仍能正常显示
距离感知
-
@Environment(\.levelOfDetail)为远处的观看者提供简化布局 -
.simplified布局使用更大的文字、更少的元素和高对比度视觉效果 -
levelOfDetail的 switch 中包含@unknown default分支
类型和布局
- 在多平台目标中,使用
#if os(visionOS)限制.systemExtraLargePortrait - 小组件内容可适配每种受支持的类型尺寸
- 通过 Xcode 预览测试所有已声明的类型尺寸
背景和渲染
- 使用
.containerBackground(for: .widget) { }标记可移除的背景 - 小组件在全彩和强调模式下均能正确渲染
- 如果前景色取决于背景,需检查
showsWidgetContainerBackground - 使用系统语义颜色,确保与玻璃质感兼容
- 确保前景在全部 7 种浅色和 7 种深色边框着色调色板下均清晰可读
参考资料
评论
支持 Markdown