Displaying a Model Object Using Bindings in a Custom NSCell from an NSTableView

I am struggling to accomplish what, in my opinion, will be a relatively common task. I have an NSTableView connected to it by an array through NSArrayController . The array controller has content configured on an NSMutableArray containing one or more instances of the NSObject model class. What I don't know how to do is expose the model inside an NSCell subclass in a way that is convenient.

For purposes of illustration, we will say that the object model is a person consisting of a name, surname, age and gender. Thus, the model will look something like this:

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

Obviously, the corresponding classes, getters init, etc. for class.

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

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

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

myPersonArray --> myPersonArrayController --> table column binding

It works great. Therefore, I delete additional columns, leaving one hidden column attached to the NSArrayController (this creates and maintains a relationship between each row and the NSArrayController ), so I fall into one visible column in the NSTableView and one hidden column. I subclass NSCell and put the appropriate drawing method to create the cell. In my awakeFromNib I set a custom subclass of NSCell :

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

This also works fine in terms of drawing. I get my custom cell displayed in the column and it repeats for each managed entity in the array controller. If I add an object or delete an object from an array controller, the table is updated accordingly.

However ... I had the impression that my PersonModel would be accessible from my NSCell subclass. But I do not know how to get to it. I don’t want to install each NSCell using setters and getters, because then I violate the concept of the whole model by storing data in NSCell instead of referencing it from the array controller.

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

In addition to searching Google and StackOverflow, I took a mandatory walk through Apple docs and didn't seem to find an answer. I found many links that hit around the bush, but nothing related to NSArrayController . The controller makes life easier when linked to other elements of the model object (for example, the "master / drilldown" scenario). I also found many links (although there are no answers) when using Core Data, but Im did not use Core Data.

In accordance with the norm, I am very grateful for any help that can be offered!

+4
source share
3 answers
Finally invented it. A man who did some things. So here is what I needed to do ...

First of all, I needed to create an array of the values ​​of my model in my model object and return these key values ​​to NSDictionary from within the model.

So my model got two new methods (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]]; } 

After embedding, I assigned a bound value in my table

arrayController --> arrangeObjects --> personDictionary .

The final step is to reference the object in NSCell drawWithFrame and use it as necessary 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 is reflected in the custom NSCell .

+3
source

There is another approach that does not require a dictionary. However, this requires an implementation of the copyWithZone: (NSZone *) zone method (ID) in your data class. For instance:

 - (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 specify the value of the Table Column in Array Controller -> assemblyObjects

Now the drawWithFrame method returns your actual object from objectValue.

 Activity *rowActivity = [self objectValue]; 

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

+1
source

I am very grateful for this post, Hooligancat, because my project was stopped on this issue 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 could not understand.

Only when I read your excellent simplification of the problem and your version of the solution that the penny fell - I am very grateful!

0
source

Source: https://habr.com/ru/post/1307215/


All Articles