Evolve your document launch experience
System Frameworks 进阶 20m

升级你的文档启动体验

Evolve your document launch experience

2024年6月10日

在 Apple 官方观看视频

一句话判断

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 角色引导设计可以作为参考。
WWDC 2024