使用 NSTableView 的自定义 NSCell 中的绑定公开模型对象

发布于 2024-08-28 21:08:11 字数 1873 浏览 4 评论 0原文

我正在努力尝试执行我认为相对常见的任务。我有一个 NSTableView 通过 NSArrayController 绑定到它的数组。数组控制器将其内容设置为一个 NSMutableArray,其中包含模型类的一个或多个 NSObject 实例。我不知道该怎么做是以绑定友好的方式公开 NSCell 子类中的模型。

为了说明的目的,我们将说对象模型是一个由名字、姓氏、年龄和性别组成的人。因此,该模型将如下所示:

@interface PersonModel : NSObject {
    NSString * firstName;
    NSString * lastName;
    NSString * gender;
    int * age;
}

显然,该类有适当的 setter、getter init 等。

在我的控制器类中,我定义了一个 NSTableViewNSMutableArray 和一个 NSArrayController

@interface ControllerClass : NSObject {
    IBOutlet NSTableView * myTableView;
    NSMutableArray * myPersonArray;
    IBOutlet NSArrayController * myPersonArrayController;
}

使用 Interface Builder,我可以轻松地将模型绑定到适当的列:

<代码>myPersonArray --> myPersonArrayController -->表列绑定

这工作正常。因此,我删除了多余的列,留下绑定到 NSArrayController 的一列隐藏(这会创建并保持每一行与 NSArrayController 之间的关联),这样我就失望了到我的 NSTableView 中的一个可见列和一个隐藏列。我创建一个 NSCell 子类并使用适当的绘图方法来创建单元格。在我的 awakeFromNib 中,我建立了自定义的 NSCell 子类:

MyCustomCell * aCustomCell = [[[MyCustomCell alloc] init] autorelease];
[[myTableView tableColumnWithIdentifier:@"customCellColumn"] 
     setDataCell:aCustomCell];

从绘图的角度来看,这也工作得很好。我的自定义单元格显示在列中,并且它对数组控制器中的每个托管对象重复。如果我添加一个对象或从数组控制器中删除一个对象,表会相应更新。

然而...我的印象是我的 PersonModel 对象可以在我的 NSCell 子类中使用。但我不知道如何实现它。我不想使用 setter 和 getter 设置每个 NSCell ,因为这样我就会通过将数据存储在 NSCell 中而不是从数组中引用它来破坏整个模型概念控制器。

是的,我确实需要一个自定义的 NSCell,因此不能选择多个列。从这里去哪里?

除了 Google 和 StackOverflow 搜索之外,我还强制浏览了 Apple 的文档,但似乎没有找到答案。我发现了很多拐弯抹角的参考资料,但没有涉及 NSArrayController 的内容。当绑定到模型实体的其他元素(例如主/详细场景)时,控制器使生活变得非常容易。我在使用Core Data时也找到了很多参考资料(虽然没有答案),但我没有使用Core Data。

按照惯例,我非常感谢您提供的任何帮助!

I am struggling trying to perform what I would think would be a relatively common task. I have an NSTableView that is bound to it's array via an NSArrayController. The array controller has it's content set to an NSMutableArray that contains one or more NSObject instances of a model class. What I don't know how to do is expose the model inside the NSCell subclass in a way that is bindings friendly.

For the purpose of illustration, we'll say that the object model is a person consisting of a first name, last name, age and gender. Thus the model would appear something like this:

@interface PersonModel : NSObject {
    NSString * firstName;
    NSString * lastName;
    NSString * gender;
    int * age;
}

Obviously the appropriate setters, getters init etc for the class.

In my controller class I define an NSTableView, NSMutableArray and an NSArrayController:

@interface ControllerClass : NSObject {
    IBOutlet NSTableView * myTableView;
    NSMutableArray * myPersonArray;
    IBOutlet NSArrayController * myPersonArrayController;
}

Using Interface Builder I can easily bind the model to the appropriate columns:

myPersonArray --> myPersonArrayController --> table column binding

This works fine. So I remove the extra columns, leaving one column hidden that is bound to the NSArrayController (this creates and keeps the association between each row and the NSArrayController) so that I am down to one visible column in my NSTableView and one hidden column. I create an NSCell subclass and put the appropriate drawing method to create the cell. In my awakeFromNib I establish the custom NSCell subclass:

MyCustomCell * aCustomCell = [[[MyCustomCell alloc] init] autorelease];
[[myTableView tableColumnWithIdentifier:@"customCellColumn"] 
     setDataCell:aCustomCell];

This, too, works fine from a drawing perspective. I get my custom cell showing up in the column and it repeats for every managed object in my array controller. If I add an object or remove an object from the array controller the table updates accordingly.

However... I was under the impression that my PersonModel object would be available from within my NSCell subclass. But I don't know how to get to it. I don't want to set each NSCell using setters and getters because then I'm breaking the whole model concept by storing data in the NSCell instead of referencing it from the array controller.

And yes I do need to have a custom NSCell, so having multiple columns is not an option. Where to from here?

In addition to the Google and StackOverflow search, I've done the obligatory walk through on Apple's docs and don't seem to have found the answer. I have found a lot of references that beat around the bush but nothing involving an NSArrayController. The controller makes life very easy when binding to other elements of the model entity (such as a master/detail scenario). I have also found a lot of references (although no answers) when using Core Data, but Im not using Core Data.

As per the norm, I'm very grateful for any assistance that can be offered!

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

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

发布评论

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

评论(3

╰つ倒转 2024-09-04 21:08:12

我非常感谢这篇文章,Hooligancat,因为我的项目在这个问题上陷入了停滞,尽管我在 http://www.timisted.net/blog/archive/custom-cells-and-core-data/
我想不通。

只是当我读到你对问题的出色简化和你的解决方案版本时,我才意识到——我非常感激!

I'm very grateful for this post, Hooligancat, because my project was stalled on this problem, and as hard as I looked at Tim Isted's identical solution at http://www.timisted.net/blog/archive/custom-cells-and-core-data/
I couldn't figure it out.

It was only when I read your excellent simplification of the problem and your version of the solution that the penny dropped - I'm very grateful!

桃气十足 2024-09-04 21:08:11

终于弄清楚了这一点。需要做一些事情的人。所以这就是我需要做的...

首先,我需要在模型对象中创建模型键值的数组,并从模型中以 NSDictionary 返回这些键值。

因此,我的模型获得了如下两个新方法(基于上面的简化示例):

+(NSArray *)personKeys
{
    static NSArray * personKeys = nil;
    if (personKeys == nil)
        personKeys = [[NSArray alloc] initWithObjects:@"firstName", @"lastName", @"gender", @"age", nil];

    return personKeys;
}

-(NSDictionary *)personDictionary
{
    return [self dictionaryWithValuesForKeys:[[self class] personKeys]];
}

实现后,我将表中的绑定值分配给

arrayController -->安排对象 --> personDictionary。

最后一步是引用 NSCell drawWithFrame 中的对象并根据需要使用,如下所示:

NSDictionary * thisCellObject = [self objectValue];

NSString * objectFirstName = [thisCellObject valueForkey:@"firstName"];
NSString * objectLastName = [thisCellObject valueForKey:@"lastName"];

正如我希望的那样,对模型对象的任何更新都会反映在自定义 NSCell

Finally figured this one out. Man that took some doing. So here is what I needed to do...

First of all I needed to create an array of my model's key values in my model object and return those key values in an NSDictionary from within the model.

Thus my model got two new methods as follows (based on my simplified example above):

+(NSArray *)personKeys
{
    static NSArray * personKeys = nil;
    if (personKeys == nil)
        personKeys = [[NSArray alloc] initWithObjects:@"firstName", @"lastName", @"gender", @"age", nil];

    return personKeys;
}

-(NSDictionary *)personDictionary
{
    return [self dictionaryWithValuesForKeys:[[self class] personKeys]];
}

Once implemented, I assign my bound value in my table to

arrayController --> arrangeObjects --> personDictionary.

The last step is to reference the object in the NSCell drawWithFrame and use as needed as follows:

NSDictionary * thisCellObject = [self objectValue];

NSString * objectFirstName = [thisCellObject valueForkey:@"firstName"];
NSString * objectLastName = [thisCellObject valueForKey:@"lastName"];

And as I hoped, any update to the model object reflects in the custom NSCell.

兰花执着 2024-09-04 21:08:11

还有另一种方法不需要字典。但是,它确实需要在数据类中实现 - (id)copyWithZone:(NSZone *)zone 方法。例如:

- (id)copyWithZone:(NSZone *)zone {
    Activity *copy = [[self class] allocWithZone: zone];
    copy.activityDate = self.activityDate;
    copy.sport = self.sport;
    copy.sportIcon = self.sportIcon;
    copy.laps = self.laps;
    return copy; }

现在在 IB 中,您将表列的值指向数组控制器 --> rangedObjects

现在,drawWithFrame 方法将从 objectValue 返回实际对象。

Activity *rowActivity = [self objectValue];

更改类需要更新 copyWithZone 方法,然后直接在 drawWithFrame 方法中访问数据。

There is another approach that does not require the dictionary. However, it does require the implementation of the - (id)copyWithZone:(NSZone *)zone method within your data class. For example:

- (id)copyWithZone:(NSZone *)zone {
    Activity *copy = [[self class] allocWithZone: zone];
    copy.activityDate = self.activityDate;
    copy.sport = self.sport;
    copy.sportIcon = self.sportIcon;
    copy.laps = self.laps;
    return copy; }

Now in IB, you point the Table Column's value at Array Controller --> arrangedObjects

The drawWithFrame method will now return your actual object from the objectValue.

Activity *rowActivity = [self objectValue];

Changing the class requires updating the copyWithZone method and then accessing the data directly in your drawWithFrame method.

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