【问题标题】:Cocoa OS X Bindings and Non-trivial data modelCocoa OS X 绑定和非平凡数据模型
【发布时间】:2015-01-10 15:07:56
【问题描述】:

这个项目是在 Cocoa 和 Swift 中为 Mac OS X 创建一个编辑器工具,它将编辑一个非平凡的数据结构。精简后的架构如下所示:

Game
    title : String
    [ Room ]

Room
    roomKey : String
    roomName : String
    roomDescription : String
    [ Object ]
    [ Exit ]

Object
    objectDescription: String

Exit 
    destinationRoomKey : String
    sourceRoomKey : String

当前的实现 - 第三次解决 - 有一个 Document.xib 文件(该应用程序是基于文档的),其中我将一个 NSObjectController 连接到由文档加载的基本游戏对象,以及一个 NSArrayController到房间的游戏对象数组。 Room 有一个 NSObjectController。我还没有完成对象或退出。

视图由基础根视图控制器处理,当您在视图层次结构中上下移动时,它会交换子视图。在根游戏状态的视图中,单击“编辑”按钮,该按钮会在房间列表的表格视图中滑动。单击其中一个房间行中的按钮会在具有自己的控制器的房间详细信息视图中滑动。

这一切都运行良好。我已经连接了房间的对象控制器,以便它使用 Interface Builder 绑定将房间数组的选定房间作为其对象。我可以这样做,因为我在一个 XIB 文件中拥有所有视图、视图控制器和数据模型控制器。

但是:现在我将游戏对象添加到此组合中,而 XIB 文件变得非常笨重。我真的觉得我想在单独的 XIB 文件中执行此操作,但是当我之前尝试过时,我无法将控制器相互连接起来。我尝试在控制器显示和删除视图的同时手动编写代码来加载和保存数据,但这很容易出错且容易出错。到目前为止,我得到的最优雅和最强大的结果是使用这种 XIB 方法。

我查看了用于绑定的编程 API,但不明白如何让它工作,或者如何发现关键路径的样子。我想如果可以以编程方式进行绑定,您可以将不同的部分放在不同的 XIB 中,并在加载时进行绑定。但我找不到任何人成功做到这一点的例子,这似乎是一条疯狂之路。

目前我对 Swift 及其与 Cocoa 和 Objective-C 的关系没有任何问题,所以如果有人在 Objective-C 或 Swift 中有答案,我会很高兴听到他们。我没有把 Swift 作为这个问题的关键词,因为它不是问题的一部分。

我已经看过 StackOverflow answer about hierarchical models,以及我目前正在做的事情,所以它没有帮助。问题在于,当有多层主从细节时,这种方法会变得笨拙。

我还看到了 StackOverflow answer about sharing controllers,这是我之前尝试过的,也是我遇到那里描述的问题的地方,如果您在 NIB 中指定控制器对象,它将被实例化为独立对象。这就是为什么我目前有巨大的死亡方法。

我可以将这个问题的标题设为“无法使编程绑定起作用”,但我不确定这是否是正确的方法。

之前肯定有人用 Cocoa 完成了使非平凡数据模型工作的工作吗?

【问题讨论】:

    标签: macos cocoa model-view-controller binding


    【解决方案1】:

    您的辅助 NIB 应该是视图 NIB,它们的所有者将是 NSViewController 的实例或自定义子类。那有一个representedObject 属性。 NIB 及其视图控制器类应被视为独立的、理论上可重用的组件。也就是说,理论上,NIB 可以在多个上下文中用于表示特定类型的对象。因此,您通常不希望连接到 UI 的其他部分或它们的控制器,除了知道这个视图正在加载代表什么对象。

    在 NIB 中,您可以使用通过 representedObject 的模型密钥路径绑定到文件所有者,或者添加绑定到文件所有者 representedObjectNSObjectController,然后使用控制器密钥通过该路径绑定您的视图选择

    当您加载这样的辅助 NIB 时,您必须将其 representedObject 设置为它应该表示的对象,该对象取自阵列控制器的选择。这应该在代码中完成,大概是决定它需要加载 NIB 并这样做的相同代码。

    如果您的 UI 设计使得细节视图需要触发最好在更高级别处理的行为 - 例如,房间视图需要安排退出视图滑入窗口,但不是它自己的视图的子视图——细节视图控制器应该定义一个委托协议并实现一个委托属性。例如,房间视图控制器的委托协议可能有一个方法-roomViewDidChangeSelectedExit:。 Room 视图控制器会在其委托上调用它,传递 self。您可以将一些协调控制器(可能是窗口控制器)设置为详细视图的委托。


    我不清楚“详细”视图和“主”视图是否同时可见。也就是说,用户可以在不先备份的情况下更改详细视图要显示的对象吗?如果是这样,有几种方法。

    您可以在加载视图时以编程方式设置绑定。这将是加载详细视图的控制器的责任。这不是详细视图控制器的责任。那没有更高层次的视角和知识来设置绑定。无论如何,你可以这样做:

    [detailViewController bind:@"representedObject" toObject:self.arrayController withKeyPath:@"selectedObjects.firstObject" options:@{ }];
    

    请务必在详细视图控制器发布之前调用-unbind:

    另一种方法是使用非绑定方法简单地观察更改的选择,并在触发的代码中设置新的representedObject。例如,如果您的主视图允许用户在表格视图中选择一个项目,您将设置表格视图的委托(几乎可以肯定已经完成)并实现-tableViewSelectionDidChange:。在该委托方法中,查询新选择的项目并将其分配给detailViewController.representedObject

    【讨论】:

    • 这看起来很棒,谢谢。我认为逃脱我的关键想法是“representedObject”的角色-按照您的描述使用它应该不会太难。我将检查我正在处理的当前位,使用这种方法对对象进行编码,并在此处提供更多详细信息。现在我对此有足够的信心点击那个勾号。再次感谢!
    • 得到这个工作,项目在这里:github.com/sarah-j-smith/Quest/blob/master/Quest/…
    猜你喜欢
    • 1970-01-01
    • 2011-06-17
    • 1970-01-01
    • 1970-01-01
    • 2014-10-11
    • 1970-01-01
    • 1970-01-01
    • 2012-10-17
    • 2011-08-31
    相关资源
    最近更新 更多