Stanford-cs193p-15|Document-Architecture

是斯坦福大学 cs193p 公开课程第 15 集的相关笔记。

cs193p 课程介绍:

The lectures for the Spring 2023 version of Stanford University’s course CS193p (Developing Applications for iOS using SwiftUI) were given in person but, unfortunately, were not video recorded. However, we did capture the laptop screen of the presentations and demos as well as the associated audio. You can watch these screen captures using the links below. You’ll also find links to supporting material that was distributed to students during the quarter (homework, demo code, etc.).

cs193p 课程网址: https://cs193p.sites.stanford.edu/2023

1. App 架构与核心协议

1.1 App 协议 (App Protocol)

  • 基础特性
    • 应用中唯一标记 @main 的结构体,作为程序入口。
    • body 不返回 View 而是返回 some Scene(场景容器)。
    • 示例代码:
      struct MemorizeApp: App {
             
        @StateObject var game = EmojiMemoryGame()
        var body: some Scene {
             
          WindowGroup {
             
            EmojiMemoryGameView(game: game)
          }
        }
      }
      

1.2 Scene 协议 (Scene Protocol)

  • 核心作用
    • 管理顶级视图的容器(如窗口、分屏视图)。
    • 可通过 @Environment(\.scenePhase) 监听场景生命周期(活跃、后台等)。
  • 常用内置场景类型
    • WindowGroup:非文档应用(如 Memorize),所有窗口共享同一 ViewModel。
      WindowGroup {
             
        EmojiMemoryGameView(game: game) // 所有窗口共享 game 对象
      }
      
    • DocumentGroup:文档应用(如 EmojiArt),每个文档独立实例化。

2. 文档架构 (Document Architecture)

2.1 DocumentGroup 的核心机制

  • 两种初始化方式
    • 新建文档DocumentGroup(newDocument: { ... })
    • 打开已有文档DocumentGroup(viewing: { ... })
  • 代码示例
  struct EmojiArtApp: App {
   
    var body: some Scene {
   
      DocumentGroup(newDocument: {
    EmojiArtDocument() }) {
    config in
        EmojiArtDocumentView(document: config.document)
      }
    }
  }
  • config.document:系统自动管理的文档实例(需符合 ReferenceFileDocument)。
  • 强制要求:实现撤销功能(Undo)以保证自动保存。

2.2 文档协议 (FileDocument vs ReferenceFileDocument)

  • FileDocument

    • 直接读写文件,实现以下方法:
      init(configuration: ReadConfiguration) throws {
             
        guard let data = configuration.file.regularFileContents else {
              throw error }
        // 从 data 初始化
      }
      
      func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
             
        FileWrapper(regularFileWithContents: /* 转为 Data */)
      }
      
  • ReferenceFileDocument(继承 ObservableObject):

    • 通过快照异步保存(避免阻塞主线程):
      func snapshot(contentType: UTType) throws -> Data {
             
        return /* 当前 Model 的 Data 表示 */
      }
      
      func fileWrapper(snapshot: Data, configuration: WriteConfiguration) throws -> FileWrapper {
             
        FileWrapper(regularFileWithContents: snapshot)
      }
      

3. 统一文件类型标识 (UTType)

3.1 定义自定义文件类型

  • 反向 DNS 命名:如 edu.stanford.cs193p.emojiart
  • Xcode 配置步骤
    1. 项目设置 → Info → Exported Type Identifiers:
      • Identifier:反向 DNS 名称。
      • Extensions:文件扩展名(如 .emojiart)。
      • Mime Types:可选 MIME 类型(如 application/emojiart)。
    2. 声明文档类型所有权:在 Document Types 中设置应用为 Owner。

3.2 代码中声明支持的 UTTypes

  • 扩展 UTType
  extension UTType {
   
    static let emojiart = UTType(exportedAs: "edu.stanford.cs193p.emojiart")
  }
  • 在文档协议中指定可处理类型
  static var readableContentTypes: [UTType] {
    [.emojiart] }
  static var writeableContentTypes: [UTType] {
    [.emojiart] }

4. 撤销机制 (Undo)

4.1 集成 UndoManager

  • 获取环境中的 UndoManager
  @Environment(\.undoManager) var undoManager  
  // 通过 Intent 传递给 ViewModel  

4.2 实现撤销操作

  • 注册撤销动作
  func undoablyPerform(operation: String, with undoManager: UndoManager?, doit: () -> Void) {
   
    let oldModel = model
    doit() // 执行修改操作
    undoManager?.registerUndo(withTarget: self) {
    target in
      target.model = oldModel // 撤销逻辑
    }
    undoManager?.setActionName(operation) // 设置撤销菜单名称(如“Undo Delete”)
  }
  • 支持重做 (Redo)
    • 通过递归注册 undo,系统自动处理 redo

5. 通知系统 (Notifications)

5.1 监听系统通知

  • 在 View 中使用 .onReceive
  .onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) {
    _ in
    // 处理应用进入后台
  }
  • 通用订阅方法
  var observer: NSObjectProtocol?
  observer = NotificationCenter.default.addObserver(
    forName: .myCustomNotification,
    object: nil,    //the broadcaster(or nil for "anyone")
    queue: .main	//the queue on which to dispatch the closure
  ) {
    notification in	//closure executed when broadcasts occur
	let info: Any? notification.userInfo
    // 处理通知
  }
  
  // 取消订阅
  NotificationCenter.default.removeObserver(observer)

5.2 发布自定义通知

  • 代码示例
  NotificationCenter.default.post(
    name
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值