SwiftUI — WindowGroup 和 DocumentGroup

在 SwiftUI 中,DocumentGroupWindowGroup 是两种不同的视图容器,用于管理应用的多个窗口/场景。它们的用途和功能有所不同,但都与多窗口或多场景支持密切相关。

具体来说:

  • DocumentGroup 是专为文档型(document-based)的应用设计的窗口管理容器。
  • WindowGroup 是一个通用的窗口场景容器,适用于任何应用场景,不限制特定类型的内容。

它们的核心区别在于用途、适用场景以及背后的功能特性。


1. 什么是 DocumentGroup #

DocumentGroup 是 SwiftUI 中专门为 文档型应用(Document-based Apps) 提供的窗口/场景容器。 它帮助开发者处理文件的创建、打开、保存等功能。这种窗口的主要特征是它基于文档模型,每一个窗口都和一个文档(文件)绑定。

适用场景: #

DocumentGroup 广泛用于需要支持多文档操作的应用,例如:

  • 文字处理器(如 Pages, Microsoft Word)
  • 代码编辑器(如 Xcode, TextEdit)
  • 图形编辑应用(如 Sketch, Photoshop)
  • 表格处理工具(如 Numbers, Excel)

DocumentGroup 的特点是,用户可以通过系统提供的文件管理功能选择打开一个文件,同时每个文档会实例化一个独立的窗口(或场景)。


如何使用 DocumentGroup #

  1. 支持文档:为了使用 DocumentGroup,你需要有一个 FileDocument 类型的数据模型。FileDocument 是 SwiftUI 提供的协议,必须实现数据的加载、保存等行为。

示例代码: #

下面是一个简单的文档型应用(iOS/macOS)的实现例子:

文档数据模型实现:

import SwiftUI
import UniformTypeIdentifiers

// 定义文档数据模型(存储为文本文件)
struct MyTextDocument: FileDocument {
    // 数据内容
    var text: String = ""

    // 定义支持的文件类型
    static var readableContentTypes: [UTType] { [.plainText] }

    // 初始化方法:从文件加载(收到数据后解码)
    init(configuration: ReadConfiguration) throws {
        if let data = configuration.file.regularFileContents,
           let string = String(data: data, encoding: .utf8) {
            text = string
        } else {
            throw CocoaError(.fileReadCorruptFile) // 解析错误抛出异常
        }
    }

    // 保存数据到文件时的行为
    func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
        let data = text.data(using: .utf8)! // 转换为 UTF-8 数据
        return FileWrapper(regularFileWithContents: data)
    }

    // 默认初始化(新文档)
    init() {}
}

DocumentGroup 主应用入口:

@main
struct MyDocumentApp: App {
    var body: some Scene {
        // 定义为 DocumentGroup
        DocumentGroup(newDocument: MyTextDocument()) { file in
            // 提供一个绑定的视图,显示文件内容
            TextEditor(text: file.$document.text)
                .padding()
        }
    }
}

代码解析: #

  1. 文件模型(MyTextDocument
  • 实现了 FileDocument 协议。
  • 定义了文件的存储和加载逻辑,支持文本文件(.txt)。
  1. DocumentGroup 场景:
  • DocumentGroup 是应用的主入口,它会自动处理文档的打开、创建等所有文件相关操作。
  • 每个文档会与一个绑定的视图实例化一个独立的窗口/场景。
  • 使用 file.$document 提供双向绑定,直接操作文件内容。

运行效果: #

  • 应用打开后,可以选择创建新文档或打开已有文档。
  • 每次打开一个新文档都会创建新窗口,并针对文件内容自动加载、显示或保存。

DocumentGroup 的优势 #

  • 系统级文件集成功能:
    包括文件打开选择器(Open/Save Panel),自动管理文件的加载和保存。
  • 多文档支持:
    每个文档都可以独立管理、绑定到一个窗口。
  • 操作简化:
    DocumentGroup 自动帮助管理文档加载、保存等核心工作,开发者重点关注界面和逻辑实现即可。

局限性: #

DocumentGroup 强依赖于文档模型(FileDocument),如果你的应用不涉及文件或文档操作,它就显得不适合。


2. 什么是 WindowGroup #

WindowGroup 是 SwiftUI 提供的通用场景容器,适用于大部分应用类型。它不依赖于文件或文档模型,可以用于管理应用中的主窗口和多窗口的应用场景。


WindowGroup 的适用场景: #

  • 非文档型应用:

  • 例如社交媒体(如 Twitter)、工具型应用(如 Notes)、游戏等。

  • 不适合用 DocumentGroup 的应用功能,通常会用 WindowGroup 来实现主界面。

  • 多窗口支持:

  • WindowGroup 可以让用户在支持多窗口的平台(macOS、iPadOS)上自由打开多个独立的窗口。

  • 主/场景容器:

  • 对于单窗口的 iPhone 应用,WindowGroup 是最常用的入口场景包装器。


如何使用 WindowGroup #

示例 1:单窗口应用 #

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView() // 应用的主界面
        }
    }
}

示例 2:多窗口支持 #

如果想让用户在 macOS 或 iPadOS 上打开多个独立的窗口,可以在 WindowGroup 中提供动态数据支持。

struct Note: Identifiable {
    var id = UUID()
    var title: String
}

@main
struct MyApp: App {
    @State private var notes: [Note] = [Note(title: "Note 1"), Note(title: "Note 2")]

    var body: some Scene {
        WindowGroup {
            List(notes) { note in
                Text(note.title)
            }
        }
    }
}

在多窗口平台上,每次用户新建一个窗口都会加载一个新的 WindowGroup 实例。


3. 两者的区别 #

特性DocumentGroupWindowGroup
用途专为文档管理设计,为每个窗口关联一个文件通用功能的场景容器,用于任何非文档型应用
依赖模型必须基于 FileDocument 模型,实现加载/保存行为不依赖特定数据模型,可以用于任意场景
文件管理能力内置文件的加载、保存、创建、打开界面无原生文件管理功能
系统集成自动与系统的文件处理机制集成(如 macOS 的 Finder)无特定集成,需手动添加文件管理功能
多窗口支持每个窗口对应一个文档支持动态生成多个不受约束的窗口
适用场景文档型应用(文字、图形、表格编辑等)非文档型应用(工具、社交、游戏等)

总结 #

  • DocumentGroup:专注于文档操作,通过 FileDocument 管理文件加载、保存。适合创建多文档型应用,提供系统文件管理集成的优越体验。
  • WindowGroup:更通用,适合大部分非文档应用(如工具、游戏等)。提供多窗口、跨场景支持,但不具备与系统文件管理的深度整合。

如果你的应用涉及文档处理,优先选择 DocumentGroup;否则,应使用更灵活的 WindowGroup

本文共 2012 字,上次修改于 Jan 5, 2025