I want to display data in an NSOutlineView. I have an NSTreeNode with data, but I don't know how to display the contents of the NSTreeNode in an NSOutlineView.
I have wasted a lot of time googling buy I couldn't found anything which could full-fill my requirement.. Can anyone help me?
The best thing to do is to study the example DragNDropOutlineView, Getting data wrapped inside the NSTreeNode is easy, you simply access to the representedObject property
For example take a look at (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item present in DragNDropOutlineView, where item is an NSTreeNode instance
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
id objectValue = nil;
SimpleNodeData *nodeData = [item representedObject];
...
...
}
Related
I am using a view-based NSTableView with only one column.
I have the following code, which returns an NSView with two NSTextFields:
- (id) tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSView *v = [[NSView alloc] init];
NSTextField *t1 = [[NSTextField alloc] init];
[t1 setStringValue:#"test1"];
NSTextField *t2 = [[NSTextField alloc] init];
[t1 setStringValue:#"test2"];
[v addSubview:t1];
[v addSubview:t2];
return v;
}
It seems that nothing happens, even if this code gets executed. I am not using the column identifier, I just want to return a custom view as a row.
This is the result:
What am I doing wrong?
This view will be very custom and large, so I cannot rely on Apple's "default" cell views.
Thank you!
View-based is a different than cell-based.
All that is different is replacing the "id" return value to "NSView".
Change this:
- (id)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
To this:
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
This is given the assumption you are running 10.7, and you are actually going to return an NSView.
A few things to check:
Does your NSTableView have its delegate set to the class that
implements the NSTableViewDelegate protocol? (I ask because
tableView:viewForTableColumn:row: is a delegate method whereas the
tableView:objectValueForTableColumn:row: is a data source method.)
Are there any bindings that are set in IB? Check mainly for bindings
to the NSTableView content binding and to the provided view's
subviews (usually the text field.)
Do you implement the numberOfRowsInTableView: method from the
NSTableViewDataSource protocol and does it return a nonzero integer?
Finally, you might have a look at the Table View Programming Guide chapter on "Populating View-Based Table Views Programmatically" in the documentation.
I created a viewController with a tableView and set the identifier of the only column to "name". Then I created an arrayController, bound it to a NSManagedObjectContext and set the right entity name.
When I now load the viewController, the tableView does display the correct amount of row. But unfortunately the cells do not contain the value of the NSManagedObjects value for the key name.
What do I have to implement in my NSManagedObject subclass or in the viewController (which is the tableViews viewController)?
I'd like to show you some code, but I don't know what could be helpful here, because it's more an conceptional question... So I'll post code as requested in comments.
UPDATE
This is the code I'm using to bind the arrayController tho the tableView:
[_tableView bind:NSContentBinding toObject:_arrayController withKeyPath:#"arrangedObjects.name" options:nil];
To inspect what the tableView gets, I added this line (after adding property called "content"):
[self bind:NSContentBinding toObject:_arrayController withKeyPath:#"arrangedObjects.name" options:nil];
In the setter I got an array containing NSString instances. But the tableView still does not display any values...
I finally used the standard NSTableViewDataSource protocol:
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [[_arrayController arrangedObjects] count];
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
return [[[_arrayController arrangedObjects] objectAtIndex:row] valueForKey:[tableColumn identifier]];
}
I think this is a solid solution, though. I might became obsessed by the -bind:toObject:withKeyPath:options: idea.
I know it should work, I can see it in sample projects, but in my sample project I can't force outline view to float group rows. The code is straightforward, everything works except that feature. Probably I'm missing something obvious.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self.outlineView setFloatsGroupRows:YES];
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{
if (item == nil)
return [#[#"1", #"2",#"3",#"4",#"5"] objectAtIndex:index];
return nil;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
return NO;
}
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
if (item == nil)
return 5;
return 0;
}
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
return item;
}
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
NSTableCellView *tableCellView = [outlineView makeViewWithIdentifier:[tableColumn identifier] owner:self];
return tableCellView;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item
{
if ([#[#"1", #"2",#"3",#"4",#"5"] indexOfObject:item] == 0)
return YES;
return NO;
}
Edit I added NSTableView alongside NSOutlineView to my project, added the same datasource and delegate methods and table view's group row flows, when outline view's one still doesn't. I'm wondering what I am missing, why outline view's group row doesn't want to fly?..
Solved. In NSOultineView group rows float only over its child rows. They don't float over other non group rows but NSTableView does. So if you have a group row without child items like in my example then it won't float.
In my opinion it's a bug or at least there shoud be an option for both cases (for at least a consistency with a table view behaviour).
Make sure self.outlineView IBOutlet is connected to the NSOutlineView in the xib.
I'm learning outline view of cocoa by Apple OS x developer library. The example source is like this:
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
return (item == nil) ? [FileSystemItem rootItem] : [(FileSystemItem *)item childAtIndex:index];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
return (item == nil) ? YES : ([item numberOfChildren] != -1);
}
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
return (item == nil) ? 1 : [item numberOfChildren];
}
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
return (item == nil) ? #"/" : [item relativePath];
}
This will list all files in my system like a tree.
The question is:
1. if there are 32 files under the "/", the method
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
will be called 62 times, I don't know why?
2. the method
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
doesn't have a parameter about the row, then how the cocoa to determine which row the item should be displayed?
if there are 32 files under the "/", the method
(id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
will be called for each row and column as it is displayed. I assume you have 2 columns and 31 rows are actually shown.
the method doesn't have a parameter about the row, then how the cocoa to determine which row the item should be displayed?
The item is a pointer to the object used to populate the table. This is all the NSOutlineViewDataSource Protocol needs to know. Details of displayed rows in internal to the NSOutlineView
I have an NSTableView, the content of which is bound to an arrayController using a filterPredicate. The tableView is view-based, so its delegate is set to an object that has the
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
method. I'm getting odd behavior, which may be related to my observation that this method is being called even when the controller's number of arrangedObjects is 0. Moreover, each time the method is called, the number of rows it asks for is the total number of objects in the array controller's content (as opposed to arrangedObjects). It think it may be because of this that when I set the objectValue of the cellView requested using
cellView.objectValue = [arrayController arrangedObjects] objectAtIndex:row];
that it is often the wrong one. Any ideas?
objectValueForTableColumn is not necessary for the case of bindings. Just banged my head against a wall for a few minutes and came back with this path of least resistance:
1) bind your NSOutlineView content to the NSTreeController (arrangedObjects controller key)
2) bind the subviews of each of the NSTableCellView to the enclosing NSTableCellView (objectValue.foo where foo is the key path for your target field)
3) set the identifier for each column in your outline view (I do this for tracking reordering of table columns between launches)
4) make sure each NSTableCellView has the identifier set to Automatic (or is the same as the table column) ** This is important and messed with me **
5) when you call makeViewWithIdentifier: make sure you pass tableColumn.identifier
This assumes you only have one cell view type per table column. Otherwise, you'll need to pass in the correct identifier for the cell view you want in makeViewWithIdentifier:.
Well, I still can't explain why the view was behaving as it was, but I did manage to get things to behave correctly, doing the following:
binding tableView content to arrayController's arrangedObjects
binding various elements of the tableCellview used in the tableView to the appropriate keyPath of the tableCellView's objectValue
providing each tableCellView as follows (where BILSelectableRoundedTableCellView happens to be a subclass of NSTableCellView):
(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSInteger numberOfObjects = [self.arrayController.arrangedObjects count];
if (numberOfObjects == 0)
return nil;
if (row >= numberOfObjects)
return nil;
BILSelectableRoundedTableCellView *viewForTableColumnRow = [self.myTableView makeViewWithIdentifier:#"DataCell" owner:self];
return viewForTableColumnRow; }
Finally, using a datasource that implements the following:
(id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSInteger numberOfObjects = [self.arrayController.arrangedObjects count];
if (numberOfObjects == 0)
return nil;
if (row >= numberOfObjects)
return nil;
return [self.arrayController.arrangedObjects objectAtIndex:row]; }
The use of the later seems contrary to the documentation out there, but is necessary to get things to work properly. Interestingly, the other datasource method (numberOfRowsInTableView) is not called when using bindings, although it is called when i'm not using bindings.