cs193p_2021笔记[7]_Document Archit

2021-11-09  本文已影响0人  walkerwzy

cs193p_2021_笔记_1
cs193p_2021_笔记_2
cs193p_2021_笔记_3_Animation_Transition
cs193p_2021_笔记_4_Color_Image_Gesture
cs193p_2021_笔记_5_Property Wrapper
cs193p_2021_笔记_6_Persistence
cs193p_2021_笔记_7_Document Architecture
cs193p_2021_笔记_8

Document Architecture

所谓的Dopcument Architecture,其实就是支持把用app产生的作品保存起来,比如你创作的一幅图片,可以保存为.jpg,你用photoshop做的文件是.psd,下次用自己的app加载这个文件,能认出所有组件和模型,比如我们想为document取个名字叫.emojiart

App Architecture

App protocol

Scene protocol

WindowGroup {return aTopLevelView}
DocumentGroup(newDocument:) { config in ... return aTopLevelView}
DocumentGroup(viewing: viewer:) { config in ... return aTopLevelView}  // 只读

当你在iPad上分屏,且两个打开同一应用,就是WindowGroup在管理,为每一个windows生成一个Scene(share the same parameter e.g. view model, 因为代码是同一份,除非额外为每个scene设置自己的viewmodel之类的).

config里保存了document(即viewModel),也保存了文件位置。

SceneStorage

[图片上传失败...(image-66d359-1636448439942)]

一个View里的@State改为@SceneStorage(uniq_id)后,app退出或crash了,仍然能找回原来的值。

这个时候每个Scene里的值就已经不一样了。

AppStorage

DocumentGroup

DocumentGroup is the document-oriented Scene-building Scene.

@main
struct MyDemoApp: App {
    @StateObject var paletteStore = PaletteStore(named: "Default")
    var body: some Scene {
        WindowGroup {
            MyDemoView()
            .environmentObject(paletteStore)
        }
    }
}

// V.S.

struct MyDemoApp: App {
    var body: some Scene {
        DocumentGroup(newDocument: {myDocument()}) { config in
            MyDemoView(document: config.document)
        }
    }
}

如果不去实现Undo,也可以直接把model存到document文件里:

  1. 你的ViewModel要能init itself from a Binding<Type>
    • config.$document
  2. ViewModel由一个ObservedObject变成一个StateObject
    • 这次必须服从FileDocument
struct MyDemoApp: App {
    var body: some Scene {
        DocumentGroup(newDocument: {myDocument()}) { config in
            // MyDemoView(document: config.document) // 之前的
            MyDemoView(document: viewModel(model: config.$document))
        }
    }
}

newDocument: {myDocument()}改为viewer: myDocument.self,就成了一个只读的model,(你甚至不需要传入实例),如果你要开发的是一个查看别人文档的应用,这个特性就比较有用了。

FileDocument protocol

This protocol gets/puts the contents of a document from/to a file. 即提供你的document读到文件系统的能力。

// create from a file
init(configuration: ReadConfiguration) throws {
    if let data = configuration.file.regularFileContents {
        // init yourself from data
    } else {
        throw CocoaError(.fileReadCorruptFile)
    }
}

// write
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
    FileWrapper(regularFileWithContents: /*my data*/)
}

ReferenceFileDocument

// 先snapshot
func snapshot(contentType: UTType) throws -> Snapshot {
    return // my data or something
}
// then write
func fileWrapper(snapshot: Snapshot, configuration: WriteConfiguration) throws -> FileWrapper {
    FileWrapper(regularFileWithContents: /* snapshpt converted to a Data */)
}

流程大概是,你的model有变化之后,会先找snapshot方法创建一份镜像,然后再要求你给出一个fileWrapper来写文件。

自定义文件类型

声明能打开什么类型的文件,通过:UTType(Uniform Type Identifier)

可以理解为怎么定义并注册(关联)自己的扩展名,就像photoshop关联.psd一样。

  1. 声明(Info tab),设置Exported/Imported Type Identifier,所以表面上的扩展名,内里还对应了一个唯一的标识符,一般用反域名的格式

    image.png
  2. 声明拥有权,用的就是上一步标识符,而不是扩展名


    image.png
  3. 告知系统能在Files app里打开这种文档

    • info.plist > Supports Document Browser > YES
  4. 代码里添加枚举:

extension UTType {
    static let emojiart = UTType(exportedAs: "edu.bla.bla.emojimart")
}

static let readableContentTypes = [UTType.emojiart]

Undo

func undoablePerform(operation: String, with undoManager: UndoManager?, doit: () -> Void){
    let oldModel = model
    doit()
    undoManager?.registerUndo(withTarget: self) { myself in
        myself.model = model
    }
    undoManager?.setActionName(operation) // 给操作一个名字,如"undo paste", 非必需
}

用`undoablyPerform(with:){} 包住的任何改变model的操作就都支持了undo

Review

回顾一下,我们把应用改造为Document Architechture的步骤:

  1. 应用入口,将WindowGroup改为了DocumentGroup,并修改了相应的传递document的方式
  2. 实现document(即view model) comform to ReferenceFileDocument
    • 实现snapshot, write to file (FileWrapper), and read from file
  3. 自定义一个文件类别(扩展名,标识符,声明拥有者等)
  4. 此时启动应用,入口UI已经是文档选择界面了,所以我说它封装了UI和行为
    • 但此时不具备保存的功能,需要进一步实现Undo'
  5. 通过undoManager把改动model的行为都包进去实现undo/redo
    • 此时document已能自动保存
  6. 增加toolbar, 实现手动undo/redo
  7. 顺便注册文档类型,以便在Files应用内能用本app打开
    • Info.plist > Supports Document Browser > YES
上一篇下一篇

猜你喜欢

热点阅读