Cocoa OS X 绑定和 Non-trivial 数据模型

Cocoa OS X Bindings and Non-trivial data model

这个项目是在 Cocoa & Swift 中为 Mac OS X 创建一个编辑器工具,它将编辑一个 non-trivial 数据结构。精简架构如下所示:

Game
    title : String
    [ Room ]

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

Object
    objectDescription: String

Exit 
    destinationRoomKey : String
    sourceRoomKey : String

当前的实现 - 第三个 go-around - 有一个 Document.xib 文件(该应用程序是基于文档的)并且我正在将 NSObjectController 连接到基础游戏 object 由文档加载,以及一个 NSArrayController 到游戏 objects 房间数组。 Room 有一个 NSObjectController。我还没有完成 objects 或退出。

视图由基本根视图控制器处理,当您在视图层次结构中上下移动时,它会交换 sub-views 进出。在根游戏状态的视图中,单击在房间列表的 table 视图中滑动的 "edit" 按钮。单击其中一排房间中的按钮会在具有自己的控制器的房间详细信息视图中滑动。

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

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

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

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

我看过 Whosebug answer about hierarchical models,这是我目前正在做的事情,所以它没有帮助。问题在于,当存在多层 master-detail.

时,这种方法会变得笨拙

我也看过 Whosebug answer about sharing controllers,这是我之前尝试过的,我 运行 进入那里描述的问题,如果你指定一个控制器 object在 NIB 中,它将被实例化为一个独立的 object。因此,为什么我现在有 huge-mega-NIB-of-death 方法。

我可以给这个问题起个标题"cannot make programmatic bindings work",但我不确定这是不是正确的方法。

肯定有人以前做过 non-trivial 数据模型与 Cocoa 一起工作的工作吗?

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

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

绑定您的视图

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

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


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

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

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

一定要在释放细节视图控制器之前调用-unbind:

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