如何使用核心数据和抽象实体创建主从接口?

发布于 2024-08-12 23:13:54 字数 2564 浏览 6 评论 0原文

苹果有一个很好的小教程,用于制作简单的主从界面。 Interface Builder 甚至会自动从核心数据实体为您生成一个。然而,我正在尝试做一些比简单示例更复杂的事情,并且我已经努力了一段时间才能让它发挥作用。

我有一个基于核心数据文档的应用程序。该模型包括一个抽象实体Page,以及Page的几个具体子实体。所有页面都有一些共同的属性(如“名称”),并且这些属性在 Page.显然,子实体具有它们独有的属性。

我希望界面允许用户查看主列表(NSTableView)中所有类型的页面。当他们选择页面时,显示的详细信息字段将取决于页面的类型。

这是我现在所拥有的:

我有一个主 nib 文件,其中显示主列表,以及页面通用的所有字段。每种页面类型都有一个笔尖及其特定字段。主 nib 文件中有主 NSArrayController,它填充 NSTableView。每个特定于页面的笔尖中也有一个 NSArrayController,以便我可以将详细信息字段绑定到当前选择的属性。我的所有 NSArrayController 的配置都是相同的,并且我将它们全部绑定到相同的 ManagedObjectContext 和相同的 SelectionIndexes。

我正在使用 Aaron Hillegass 在他的 Cocoa 书中描述的视图交换方法。因此,我注册了 NSTableViewSelectionDidChangeNotifications,当我收到通知时,它会调用 switchView 方法:

switchView 查看当前选定的对象,检查它是哪种类型的 Page,并根据 Hillegass 的方法交换适当的 nib 文件。

如果我只添加一种类型的页面,一切正常,但一旦添加第二种类型的页面,我就会收到此错误:

对象的关键路径selectionIndexes设置值错误(来自绑定对象实体:Page,所选对象数量:1):[ valueForUndefinedKey:]:实体NoColPage不符合键侧键值编码。

错误的最后一部分是有道理的:它试图显示错误的笔尖,因此它试图绑定到该对象不存在的字段。

我向 MyDocument 添加了一个 SelectionIndexes 字段,以便我的所有 NSArrayController 都可以绑定到同一个位置。我为这个问题苦恼了好几天了,一直想不通。有什么想法吗?

如果有帮助,这里是您可以下载的示例项目。我仅将与此问题相关的内容从项目中提取到一个新的虚拟应用程序中,我一直在用它来测试和使用。

PS:Interface Builder 用于从核心数据实体生成主从接口的工具无法像我希望的抽象实体那样工作。它仅为超实体中的属性创建字段。

编辑:我认为约书亚在某些事情上,但不幸的是,它不起作用——我一直遇到同样的问题。起初我很困难,因为我不明白 -unbind: 需要一个字符串常量,而不是一个关键路径。

我尝试了几种变体:我跟踪当前显示的笔尖的数组控制器;我跟踪当前显示的页面类型,并且仅在尝试显示不同的页面类型时取消绑定/重新绑定...

这是代码的相关部分。

-(void) displayViewController: (ManagingVC *) vc withClass:(NSString*) className {

//Try to end editing
NSWindow *w = [box window];
BOOL ended = [w makeFirstResponder:w];
if (!ended) {
    NSBeep();
    return;
}

//The Managing View Controller's NSArrayController
NSArrayController* vcAc = [vc arrCont];

//if the page we're trying to switch to is NOT the same type as the current page we're displaying
//if it is, do nothing.
if (![currPageDisplayed isEqual:className]) {

    //unbind the old view controller
    ManagingVC *oldvc = [viewControllers objectForKey:className];
    NSArrayController* oldsac = [oldvc arrCont];
    [oldsac unbind:@"selectionIndexes"];

    //bind the new view controller
    [vcAc bind:@"selectionIndexes" toObject:self withKeyPath:@"selectionIndexes" options:nil];
    currPageDisplayed = className;

    NSView *v = [vc view];

    //display the new view in an NSBox in the main nib
    [box setContentView:v];
}

}

Apple has a nice little tutorial for making a simple master-detail interface. Interface Builder will even automatically generate one for you from a Core Data entity. However, I'm trying to do something more complicated than the simple example and I've been struggling for a while to get it to work.

I have a Core Data document-based application. The model includes an abstract entity Page, and several concrete subentities of Page. All Pages have some attributes in common (like "name") and those are defined in Page. Obviously, the subentities have the attributes that are unique to them.

I want the interface to allow the user to see all types of Pages in the master list (an NSTableView). When they select a Page, the detail fields that are displayed would depend on what kind of Page it is.

Here's what I have right now:

I have a main nib file, where the master list is displayed, plus all fields common to a Page. There's a nib for each page type with its specific fields. There's the main NSArrayController in the main nib file, which is populating the NSTableView. There's an NSArrayController in each of the page-specific nibs as well so that I can bind the detail fields to attributes of the current selection. All of my NSArrayControllers are configured identically, and I have them all bound to the same managedObjectContext and the same selectionIndexes.

I am using Aaron Hillegass's method for view swapping that he describes in his Cocoa book. So I registered for NSTableViewSelectionDidChangeNotifications, and when I receive one, it calls a method switchView:

switchView looks at the currently selected object, checks which type of Page it is, and swaps in the appropriate nib file according to Hillegass's method.

Everything works fine if I only add pages of one type, but as soon as I add a page of a second type, I get this error:

Error setting value for key path selectionIndexes of object (from bound object entity: Page, number of selected objects: 1): [ valueForUndefinedKey:]: the entity NoColPage is not key value coding-compliant for the key side.

The last part of the error makes sense: it's stuck trying to display the wrong nib, so it's trying to bind to fields that don't exist for this object.

I added a selectionIndexes field to MyDocument so that all of my NSArrayControllers could bind to the same place. I've agonized over this for days and I can't figure it out. Any ideas?

If it helps, here is a sample project you can download. I extracted only the things relevant to this problem from my project into a new dummy application, which I've been using to test and play around.

PS: Interface Builder's tool for generating a master-detail interface from a Core Data entity doesn't work like I want it to for abstract entities. It only creates fields for the attributes in the superentitity.

Edit: I think Joshua is on to something, but unfortunately, it doesn't work--I keep running into the same problem. At first I was having a hard time because I didn't understand that -unbind: expects a string constant, not a key path.

I've tried several variations: where I keep track of the currently displayed nib's array controller; where I keep track of the currently displayed page type and only unbind/rebind when I'm trying to display a different page type...

Here's the relevant section of code.

-(void) displayViewController: (ManagingVC *) vc withClass:(NSString*) className {

//Try to end editing
NSWindow *w = [box window];
BOOL ended = [w makeFirstResponder:w];
if (!ended) {
    NSBeep();
    return;
}

//The Managing View Controller's NSArrayController
NSArrayController* vcAc = [vc arrCont];

//if the page we're trying to switch to is NOT the same type as the current page we're displaying
//if it is, do nothing.
if (![currPageDisplayed isEqual:className]) {

    //unbind the old view controller
    ManagingVC *oldvc = [viewControllers objectForKey:className];
    NSArrayController* oldsac = [oldvc arrCont];
    [oldsac unbind:@"selectionIndexes"];

    //bind the new view controller
    [vcAc bind:@"selectionIndexes" toObject:self withKeyPath:@"selectionIndexes" options:nil];
    currPageDisplayed = className;

    NSView *v = [vc view];

    //display the new view in an NSBox in the main nib
    [box setContentView:v];
}

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

诗酒趁年少 2024-08-19 23:13:54

问题是您将笔尖的数组控制器绑定到文档的选择,这导致它们尝试(当选择更改时)表示所选项目。

试试这个:

  1. 取下笔尖上的固定器。
  2. 在添加新视图之前,请在代码中连接其绑定。
  3. 在删除旧视图之前,请在代码中断开其绑定。

这应该让一切都愉快。

The problem is you're leaving your nibs' array controllers bound to the document's selection, which is causing them to attempt (when the selection changes) to represent the selected item.

Try this:

  1. Remove the bindings in the nibs.
  2. Before adding a new view, connect its bindings in code.
  3. Before removing the old view, disconnect its bindings in code.

This should keep everything happy.

梦途 2024-08-19 23:13:54

在花费了太多时间尝试使每个 NIB 的阵列控制器同步之后,我放弃了这种方法。唯一对我有用的是以编程方式控制哪些 GUI 元素显示在主笔尖中及其绑定。这意味着消除其他笔尖。如果您使用的不仅仅是几个文本字段,这并不是一个真正可持续的解决方案,但它现在对我有用。

我仍然遵循 Joshua 的建议,在切换视图之前取消绑定,但现在我只将文本字段绑定到 arrayController.selection.whateverKey

After spending way too much time trying to get each NIB's array controller in sync, I've given up on that approach. The only thing that has worked for me is programmatically controlling which GUI elements are displayed in the main nib, and their bindings. This means eliminating the other nibs. This isn't really a sustainable solution if you're working with more than just a couple of text fields, but it works for me right now.

I still follow Joshua's advice of unbinding before switching the view, but right now I'm only binding the text fields to arrayController.selection.whateverKey

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文