使用 Interface Builder、NSObjectController 子类和绑定时在模型和视图之间插入控制器逻辑

Posted

技术标签:

【中文标题】使用 Interface Builder、NSObjectController 子类和绑定时在模型和视图之间插入控制器逻辑【英文标题】:Inserting controller logic between model and view when using Interface Builder, NSObjectController sub-classes, and bindings 【发布时间】:2012-12-07 11:16:49 【问题描述】:

我一直在努力理解在使用 IB、NSObjectController 子类和绑定时插入控制器逻辑的最佳方式。

我需要在模型和视图之间插入控制器逻辑,我正在努力寻找一种优雅的方式来做到这一点。是的,您可以向文件所有者发送操作并在其中处理控制器逻辑,但是当某些核心数据模型可以扩展到五十个或更多具有深度关系结构的实体时,这开始安装大量的样板代码。

一个非常简单的例子是这样的;假设您有一个具有四个字符串属性 myTextWinter、myTextSpring、myTextSummer、myTextAutumn 的实体。您有一个通过 NSObjectController 连接到 IB 中的视图。现在,假设用户可以选择他们希望通过选择春天,夏季,秋季,从某个地方选择哪个“季节” - 选择该季节,我想显示相应的季节文本。

在这个简化的示例中,我可能会在 NSDocument 子类中获取对象,创建一个名为 mySeasonText 的属性,我在视图中绑定到该属性,然后检查我的 NSUserDefaults 是否有适当的季节并将请求路由到适当的属性在模型中。

当我有 50 个实体时,问题就出现了,其中一些实体的关系有两个、三个或更深,每个实体都有自己的一组特定于季节的文本属性,我希望在从季节菜单中进行选择时在这些属性之间进行切换。或者,如果我有一堆 nsarraycontroller 链接在一起以访问更深层次的对象。

迄今为止,我一直在做以下事情;在我的每个模型对象中添加一个名为“mySeasonText”的属性,然后从我的控制器设置中获取设置,然后路由到适当的季节。 I refresh these objects whenever a new item in the menu is selected.

虽然这有效并消除了绝对大量的样板代码,但我的控制器逻辑现在在我的模型中。

一定有更好的方法!有人可以指点我正确的方向吗?

【问题讨论】:

文字过多。你应该更简洁地提出这个问题。 谢谢,已编辑。作为参考,这是我已经尝试过的(从上面的答案中删除)我还尝试将 nsarraycontroller 子类化(没有任何运气)以使用知道当前的代理对象“包装”数组中的每个对象'season' 设置,然后将 setValueForUndefinedKey: 和 valueForUndefinedKey: 覆盖到包装对象中的适当属性。这感觉就像一个讨厌的 hack,我不确定它是否会起作用。 【参考方案1】:

这是一个棘手的话题。 Apple 甚至在自己的文档中提到了这些挑战:

通过使用绑定技术,您可以轻松创建一个 Cocoa MVC 应用程序,其视图直接观察模型对象以接收状态更改通知。然而,这种设计存在理论上的问题。视图对象和模型对象应该是应用程序中最可重用的对象。 [...] 在设计方面,最好将模型和视图对象分开,因为这样可以提高它们的可重用性。

您正在寻找的设计模式是Mediating Controller - 一种使用可可绑定类插入控制器逻辑的方法:

中介控制器通常是您从 Interface Builder 库中拖动的现成对象。您可以配置 [Mediating controllers] 以在视图对象的属性和控制器对象的属性之间建立绑定,然后在这些控制器属性和模型对象的特定属性之间建立绑定。因此,当用户更改视图对象中显示的值时,新值会自动传递给模型对象进行存储——通过中介控制器;并且当模型的属性更改其值时,该更改会传达给视图以进行显示。

以下是我对他们的看法:您是否看过电影或电视节目中两个角色需要交谈,但他们不会说任何相同的语言?他们找到其他人(或喜剧中的另外 5 个人),每个人都有一种共同语言,他们通过玩翻译电话的巨大游戏进行交流。

中介控制器有点像这样。

随着您的应用程序的增长,他们会了解所有关于在哪里查找的超级具体规则在这一视图中查找这一件事。这是应用程序需要运行的那种代码,但是当你把它放到你的模型中时,你会觉得它是讨厌的。

对于几个具体和详细的​​示例,Apple 提供了这个非常详细的文档:Bindings Message Flow。

有关此和相关 MVC + 绑定的一些非常好的讨论,请参阅:

MVC and cocoa bindings best practices question Why use NSObjectController? Replacing model objects using an NSArrayController

【讨论】:

感谢您的回答。我同意,我需要一个中介控制器,听起来这个中介控制器应该处理另一个控制器对象,然后转发给模型。但是这在实践中会是什么样子?最好扩展 NSObjectController 和 NSArrayController ,它们已经构建为与 Core Data 一起使用,并且在数组的情况下“处理诸如排序、选择、添加和删除对象之类的任务”。他们实际上确实使用代理对象来包装模型对象。但是我该如何自己做呢? 中介控制器链可以非常简单也可以非常复杂——因此这取决于您的特定需求和架构。一个简单的例子是,如果我有一个非常复杂的视图,比如通知区域、设置区域和消息区域。我可以为该视图制作一个中介控制器,而不是处理一堆模型,而是与通知控制器、设置控制器和消息控制器对话——每个控制器只与少数模型对话。这样相关的代码就被保存在一起,并且很容易在其他地方重用控制器。

以上是关于使用 Interface Builder、NSObjectController 子类和绑定时在模型和视图之间插入控制器逻辑的主要内容,如果未能解决你的问题,请参考以下文章

使用 Interface Builder 进行切换

iOS Update Interface Builder视图框架基于约束

在 Interface Builder 中使用 UITapGestureRecognizers 的问题

Purelayout™ 可以通过 Interface Builder 使用吗?

在 Interface Builder 中使用常量

在 Interface Builder 中使用“属性”标签文本