升级你的文档启动体验
Evolve your document launch experience
2024年6月10日
一句话判断
iOS 18 为文档类 App 提供了一套全新的可定制启动界面,从系统文档浏览器变成了带有品牌装饰、模板选择和自定义背景的沉浸式首页——重新编译就能获得。
这场 Session 讲了什么
文档类 App(像 Keynote、Pages、Swift Playgrounds)之前的启动体验是系统级的文档浏览器,千篇一律。iOS 18 引入了全新的 DocumentGroupLaunchScene,让你可以完全定制这个启动界面:自定义背景(纯色、渐变、图片、自定义 View)、前景和背景装饰元素、按钮文案、以及从模板创建文档的能力。
最关键的是,迁移成本极低。SwiftUI 的 DocumentGroup 只要用 iOS 18 SDK 重新编译,不用改任何代码,就会自动显示新的启动界面。UIKit 的 UIDocumentViewController 也支持——把它设为 window 的 rootViewController 就行。
Session 用一个儿童故事创作 App 作为演示,展示了如何添加装饰角色、自定义背景、模板选择等完整流程。
值得深挖的点
从系统浏览器到品牌化首页的设计跃迁
新启动体验的设计语言和传统的 UIDocumentBrowserViewController 完全不同。之前是纯功能性的文件列表,现在是一个有层次感的视觉空间:最前面是装饰性的前景元素(如 Swift Playgrounds 的 Byte 角色),中间是标题区域(App 名称 + 操作按钮),后面是背景装饰,底层是可以替换的自定义背景。
accessoryView builder 提供了几何信息(场景边界和标题区域的 frame),让你可以用 position() 或 offset() 精确定位装饰元素。这种”画布式”布局给了设计师极大的自由度——你可以把品牌角色放在标题左侧,把装饰花纹放在右侧,用自定义视图填充整个空间。
按钮系统支持主按钮和次按钮。主按钮默认是”创建文档”,可以自定义文案。次按钮可以用 actionButton 添加,比如”从模板创建”。当用户点击模板按钮时,SwiftUI 调用一个 async 闭包,你在里面返回基于模板创建的文档。如果需要先展示模板选择器,可以用 withCheckedContinuation 把异步流程挂起,等用户选完再 resume。
模板创建的 Continuation 模式
模板创建的 API 设计用了 Swift 的 structured concurrency + continuation 模式。核心思路是:SwiftUI 通过 async 闭包向你要一个文档,但你可能需要先弹出一个 Sheet 让用户选模板。withCheckedContinuation 让你把闭包的返回值”推迟”到用户选择完成之后。
这种模式比回调更安全——编译器会检查 continuation 是否恰好被 resume 一次。忘记 resume 会导致闭包永远挂着,多次 resume 会触发运行时错误。在实践中,确保在 Sheet 的 dismiss 回调和取消回调中都正确处理 continuation 是关键。
代码片段
声明自定义启动场景
@main
struct WritingApp: App {
var body: some Scene {
DocumentGroupLaunchScene("我的故事") {
// 主按钮:创建新文档
NewDocumentButton("开始创作")
} background: {
// 自定义背景图片
Image("alien-jungle-background")
.resizable()
.ignoresSafeArea()
} accessoryView: { geometry in
// geometry 包含 sceneFrame 和 titleFrame
// 精确定位装饰元素
Image("robot-character")
.position(x: geometry.titleFrame.minX - 60,
y: geometry.titleFrame.midY)
Image("alien-flower")
.position(x: geometry.titleFrame.maxX + 40,
y: geometry.titleFrame.midY + 20)
}
}
}
场景:为文档 App 创建品牌化的启动界面。坑:装饰元素的位置是绝对坐标,不同设备尺寸需要测试布局是否合理。
实现模板创建功能
DocumentGroupLaunchScene("我的故事") {
NewDocumentButton("开始创作")
// 次按钮:从模板创建
} documentGroup: { intent in
// 处理文档创建意图
} templates: {
ActionButton("选择模板") {
// 用 continuation 延迟返回,先展示模板选择器
return await withCheckedContinuation { continuation in
// 保存 continuation,在 Sheet 中使用
self.templateContinuation = continuation
self.showTemplatePicker = true
}
}
}
// 模板选择器中的回调
func onTemplateSelected(_ template: StoryTemplate) {
let document = StoryDocument(from: template)
templateContinuation?.resume(returning: document)
templateContinuation = nil
}
场景:用户从预设模板创建文档。坑:取消选择时也需要 resume(返回 nil 或默认文档),否则 continuation 泄漏。
UIKit App 的迁移方式
// UIKit 迁移:把 UIDocumentViewController 设为 root
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, ...) {
guard let windowScene = scene as? UIWindowScene else { return }
let documentVC = DocumentViewController()
// 配置启动选项
documentVC.launchOptions.primaryAction = .createDocument(
intent: UIDocument.CreationIntent(title: "新建故事")
)
window?.rootViewController = documentVC
}
}
场景:UIKit 文档 App 启用新启动界面。坑:必须移除原来的 UIDocumentBrowserViewController 作为 root,新的启动体验自带浏览器。
最佳实践
新项目: 直接用 DocumentGroupLaunchScene 定义启动界面。设计上建议克制使用装饰元素——一两个有辨识度的品牌元素就够了,过度装饰会分散注意力。模板功能对文档类 App 很有价值,建议从 3-5 个精心设计的模板开始。
已有项目: 最快的方式是先用 iOS 18 SDK 重新编译,零代码改动就能获得新的启动界面。然后逐步添加背景自定义、装饰元素和模板功能。UIKit App 需要把 UIDocumentViewController 提升为 root,这个改动涉及视图层级重组,建议在功能分支上先验证。
还有什么值得关注
- 新启动界面内置了文档浏览器,支持按文件格式筛选。
- 背景可以是任意 SwiftUI View,这意味着你可以放动画、甚至实时渲染的 3D 场景。
- Swift Playgrounds 是第一个采用新启动体验的 App,它的 Byte 角色引导设计可以作为参考。