Highlight
UIKit 应用无需重写即可运行在 visionOS 上:添加新目标、处理不可用的 API、使用语义颜色和 hover 效果适配平台风格,再通过 Ornament 和 RealityKit 将内容扩展到三维空间。
核心内容
第一步:把 iPad 应用搬上 visionOS
你有一个用 UIKit 写的 iPad 应用,想让它在 visionOS 上运行。第一步是在 Xcode 的 General 标签页里添加新的运行目标。
visionOS 的应用图标也很特别。它由三张图片叠加而成,当用户注视图标时会产生动态响应效果。你需要在 Asset Catalog 中添加这套分层图标。
(02:01)
添加目标后直接编译,可能会遇到报错。visionOS 是一个全新平台,有些 API 不再适用。
处理不可用的 API
visionOS 不支持 iOS 14 之前废弃的 API。此外,一些 API 因平台特性差异被标记为不可用:
- UIDeviceOrientation:假设设备可以旋转,不适用于 visionOS
- UIScreen:单屏幕表示的概念在这个平台上不成立
- UITabBar 的 leadingAccessoryView/trailingAccessoryView:Tab Bar 的布局方向不同
(03:22)
示例中的像素画应用使用了 UIPencilInteraction,visionOS 不支持 Apple Pencil,这个 API 不可用。解决方案是用条件编译把它排除:
#if !os(xrOS)
// Apple Pencil interaction code
let pencilInteraction = UIPencilInteraction()
pencilInteraction.delegate = self
view.addInteraction(pencilInteraction)
#endif
修复编译错误后,应用就能在 Shared Space 中运行了。玻璃材质背景、hover 效果都是系统默认提供的。
语义颜色与材质:让应用融入平台
应用在 iPad 上用黑色标题文字配白色背景看起来很好,但在 visionOS 的玻璃背景上显得突兀。
语义颜色(semantic colors)在这个平台上尤为重要。UIColor.label、UIColor.secondaryLabel 等系统颜色会自动适配平台、外观和辅助功能设置。UILabel 使用语义颜色时默认获得 vibrancy 效果。
(06:58)
Hover 效果:让交互可感知
visionOS 上,用户注视一个元素时会产生 hover 效果。系统控件默认带有 hover 效果,你也可以为自定义视图添加。
UIView 新增了 hoverStyle 属性。可以设置为 .highlight 或 .lift 效果,也可以用 UIShape API 自定义形状。设置为 nil 则移除 hover 效果。
(10:49)
输入方式的变化
visionOS 引入了全新的输入系统:
- 注视 + 手指捏合释放 = Tap 手势
- 注视 + 捏合并移动手 = Pan 手势
- 近距离直接触摸 = 物理触摸
- 触控板 = 配对后可用
- 辅助功能 = VoiceOver 和 Switch Control 均支持
平台最多支持两个同时输入(每只手一个)。示例应用原来用四指轻扫删除所有内容,在 visionOS 上改为双指即可。
(12:17)
详细内容
空间化 UIKit presentations
UIKit 的 presentations 在 visionOS 上获得了空间化风格:
Sheet:推出时把 presenting view controller 向后推并变暗。与 iPad 不同,点击 sheet 外部不会自动关闭。
Alert:顶部放置应用图标的 2D 表示。总是从应该被推后的 view controller 呈现。
Popover:在 visionOS 上不受 scene 边界限制,可以超出应用窗口。但要注意 permittedArrowDirections 的设置。
(14:26)
代码示例:修复 Popover 箭头方向
import UIKit
extension EditorViewController {
@objc func showDocumentPopover(sender: UIBarButtonItem) {
let controller = DocumentInfoViewController(document: pixelDocument)
controller.modalPresentationStyle = .popover
if let presentationController = controller.popoverPresentationController {
presentationController.barButtonItem = sender
if traitCollection.userInterfaceIdiom == .reality {
presentationController.permittedArrowDirections = .any
} else {
presentationController.permittedArrowDirections = .right
}
}
present(controller, animated: true, completion: nil)
}
}
关键点:
traitCollection.userInterfaceIdiom == .reality检测是否在 visionOS 上运行- visionOS 上 popover 不受 scene 边界限制,使用
.any让系统选择最佳位置 - iPad 上保持
.right限制,避免 popover 超出屏幕
Ornament:把内容放到场景之外
Ornament 是 visionOS 的新概念,允许内容放置在应用 scene 的周围。系统组件也在用:SwiftUI 的 TabView 把 tab bar 放在 leading edge 的 ornament 中,Safari 用 ornament 放置导航栏,Freeform 用 ornament 做底部工具栏。
Ornament 抬升于主内容前方,增加了深度感。它们与 view controller 的生命周期绑定,sheet 呈现时会保持相对位置。
(17:15)
代码示例:创建 Ornament
import UIKit
import SwiftUI
extension EditorViewController {
func showEditingControlsOrnament() {
let ornament = UIHostingOrnament(
sceneAlignment: .bottom,
contentAlignment: .center
) {
EditingControlsView(model: controlsViewModel)
.glassBackgroundEffect()
}
self.ornaments = [ornament]
editorView.style = .edgeToEdge
}
}
关键点:
UIHostingOrnament托管 SwiftUI 内容,需要import SwiftUIsceneAlignment: .bottom把 ornament 放在 scene 底部边缘contentAlignment: .center让内容居中于边缘.glassBackgroundEffect()为 ornament 添加玻璃背景ornaments是 UIViewController 的新属性,接受 ornament 数组- ornament 与 view controller 生命周期绑定,移除 view controller 时 ornament 自动消失
代码示例:使用语义颜色
private let titleLabelTextField: UITextField = {
let textField = UITextField()
textField.textColor = UIColor.label
return textField
}()
private let authorLabel: UILabel = {
let label = UILabel()
label.textColor = UIColor.secondaryLabel
return label
}()
关键点:
UIColor.label是主文本颜色,自动适配平台和外观UIColor.secondaryLabel用于辅助文本,降低视觉层级- 使用语义颜色的 UILabel 自动获得 vibrancy 效果
- 避免硬编码 RGB 值,让应用在不同平台上保持一致外观
代码示例:为文本框添加凹陷外观
textField.borderStyle = .roundedRect
关键点:
- visionOS 上
.roundedRect会自动呈现凹陷(recessed)外观 - 这是系统为该平台设计的默认样式,无需额外配置
代码示例:自定义容器背景样式
class MyViewController: UIViewController {
override var preferredContainerBackgroundStyle: UIContainerBackgroundStyle {
return .glass
}
}
关键点:
preferredContainerBackgroundStyle是 UIViewController 的新属性- 可选值:
.automatic、.glass、.hidden - UINavigationController 和 UISplitViewController 默认使用玻璃背景
代码示例:自定义 Hover 效果
class CollectionViewCell: UICollectionViewCell {
init(document: PixelArtDocument) {
super.init(frame: .zero)
self.hoverStyle = .init(
effect: .highlight,
shape: .roundedRect(cornerRadius: 8.0)
)
}
}
关键点:
hoverStyle是 UIView 的新属性effect可选.highlight或.liftshape使用UIShapeAPI,支持圆角矩形等形状- 设置为
nil可移除 hover 效果
代码示例:根据平台调整手势
func fourFingerSwipe() {
let gesture = UISwipeGestureRecognizer(
target: self,
action: #selector(self.deleteAll)
)
gesture.direction = .left
if traitCollection.userInterfaceIdiom == .reality {
gesture.numberOfTouchesRequired = 2
} else {
gesture.numberOfTouchesRequired = 4
}
self.view.addGestureRecognizer(gesture)
}
关键点:
traitCollection.userInterfaceIdiom == .reality是检测 visionOS 的推荐方式- visionOS 最多支持两个同时输入,所以双指轻扫足够
- iPad 上保持四指轻扫,避免与其他手势冲突
在 UIKit 中使用 RealityKit
RealityView 是新的 SwiftUI view,用于托管 RealityKit 内容。通过 UIHostingController,你可以在 UIKit 应用中嵌入 RealityView,无需重写整个应用。
(21:56)
代码示例:在 UIKit 中嵌入 RealityView
import UIKit
import SwiftUI
import RealityKit
extension EditorViewController {
func showEntityPreview() {
let entityView = PixelArtEntityView(model: entityViewModel)
let controller = UIHostingController(rootView: entityView)
addChild(controller)
view.addSubview(controller.view)
controller.didMove(toParent: self)
prepareEditorInteractions()
}
}
关键点:
UIHostingController将 SwiftUI view 嵌入 UIKit 视图层级PixelArtEntityView内部使用RealityView渲染 RealityKit 实体- 标准的 child view controller 生命周期管理:
addChild→addSubview→didMove(toParent:) - 这种方式让现有 UIKit 应用无需重写即可获得 3D 能力
核心启发
-
把现有 iPad 应用移植到 visionOS
- 做什么:将已有的 UIKit 应用(如绘图工具、笔记应用、阅读器)添加 visionOS 目标,让它在空间中运行
- 为什么值得做:UIKit 应用几乎零成本获得全新的运行环境,用户可以在更大的虚拟空间中使用你的应用
- 怎么开始:在 Xcode 的 General 标签页添加 visionOS 目标,处理编译错误(废弃 API 和平台不兼容 API),使用语义颜色替换硬编码颜色
-
用 Ornament 重新设计工具栏
- 做什么:把传统底部工具栏或侧边栏提取为 Ornament,让主内容区域最大化
- 为什么值得做:Ornament 突破了 2D 窗口的边界,工具控件悬浮在主内容周围,既不遮挡又能快速访问
- 怎么开始:用
UIHostingOrnament包裹 SwiftUI 工具栏视图,设置合适的sceneAlignment和contentAlignment,给 ornament 添加.glassBackgroundEffect()
-
为应用添加 3D 预览功能
- 做什么:在 UIKit 应用中嵌入 RealityView,让用户可以从任意角度查看 3D 内容
- 为什么值得做:不需要重写整个应用,只需一个
UIHostingController就能在现有界面中注入 3D 能力 - 怎么开始:创建 SwiftUI view 包含
RealityView,用UIHostingController嵌入到 UIKit 的 view controller 中,通过 child view controller 生命周期管理
-
设计空间化的 Popover 交互
- 做什么:利用 visionOS 上 popover 不受 scene 边界限制的特性,设计更灵活的上下文菜单
- 为什么值得做:popover 可以延伸到应用窗口之外,提供更自然的指向性交互
- 怎么开始:检查
permittedArrowDirections,在 visionOS 上使用.any让系统自动选择最佳位置,避免硬编码平台假设
关联 Session
- Meet SwiftUI for spatial computing — SwiftUI 在空间计算平台上的基础介绍
- Build spatial experiences with RealityKit — 深入了解 RealityKit 和 RealityView 的用法
- Principles of spatial design — 空间设计原则,指导如何设计 visionOS 应用
- Elevate your windowed app for spatial computing — 将窗口化应用提升到空间计算环境的进阶指南
评论
GitHub Issues · utterances