美文网首页
cs193p_2021笔记[7]_Document Archit

cs193p_2021笔记[7]_Document Archit

作者: walkerwzy | 来源:发表于2021-11-09 17:01 被阅读0次

    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

    • 一个app里只能有一个struct服从App Protocol
    • mark it with @main
    • it's var body is some Scene

    Scene protocol

    • A Scene is a container fo a top-lever View that you want to show in your UI
    • @Environment(\.scenePhase)
    • three main types of Scenes:
    WindowGroup {return aTopLevelView}
    DocumentGroup(newDocument:) { config in ... return aTopLevelView}
    DocumentGroup(viewing: viewer:) { config in ... return aTopLevelView}  // 只读
    
    • 后两个类似view里面的ForEach但不完全相同:
      • 而是:"new window" on Mac, "splitting the screen" on iPad -> for create new Scene
    • content参数是一个返回some View的方法
      • 返回的是top-level view
      • 每当新建一个窗口或窗口被分割时都会被调用

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

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

    SceneStorage

    • 能持久化数据
    • 以窗口/分屏为单位 -> per-Scene basis
    • 也会invalidate view
    • 数据类型有严格限制,最通用的是RawRepresentable

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

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

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

    AppStorage

    • application-wide basis
    • 存在UserDefaults里
    • 服从@SceneStorage的数据才能被存储
    • invalidate view

    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)
            }
        }
    }
    
    • 不再用@StateObject传递ViewModel,每新建一个Document都会有一个独立的ViewModel
      • 必须要服从ReferenceFileDocument(这样能存到文件系统以及从文件系统读取了)
      • config参数包含了这个ViewModel(就是document),以及document的url
      • 很好理解,每一个document肯定有自己的数据(想象一个“最近打开”的功能,每一个文档都是独立的)
    • newDocument里自行提供一个新建document的方法
    • 封装了关联的(选择document的)UI和行为
    • you MUST implement Undo in your application

    如果不去实现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

    • 几乎和FileDocument一致
    • 继承自ObservableObject -> ViewModel only
    • 唯一的区别是通过后台线程的一个snapshot来写入
    // 先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

    • use ReferenceFileDocument must implement Undo
    • 这也是SwiftUI能自动保存的时间节点
    • by UndoManager -> @Environment(\.undoManager) var undoManager
    • and by register an Undo for it: func registerUndo(withTarget: self, howToUndo: (target) -> Void)
    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

    相关文章

      网友评论

          本文标题:cs193p_2021笔记[7]_Document Archit

          本文链接:https://www.haomeiwen.com/subject/iebdzltx.html