Just getting into NSOutlineViews and see them a useful control.
Is it possible to show a Xib as the root item??
Fritzables.
Yes, when you use a view based NSOutlineView. First register the nib you want to display for a cell using registerNib:forIdentifier: (windowDidLoad in a window controller would be a good place, awakeFromNib is also a possibility).
NSNib *cellNib = [[NSNib alloc] initWithNibNamed:#"MyCell" bundle:nil];
[self.outlineView registerNib:cellNib forIdentifier:#"MyIdentifier"];
Next in your outlineView:viewForTableColumn:item: you get a (possibly recycled) instance of your nib by using the earlier specified identifier:
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
NSView *cellView = [outlineView makeViewWithIdentifier:#"MyIdentifier" owner:self];
// optional configuration here
return cellView;
}
I've got a blog post + mac app sample code that demonstrates this.
Yes. You just have to switch it to use view based cells.
In your delegate, implement outlineView:viewForTableColumn:item: to provide the correct XIB.
Yes it is Possible, NSOutlineView is another great visual control available in Mac OS X. Being descendant of the NSTableView It represents the hierarchical data. You can collapse and expand nodes, see parents and their children. In this article we will describe how to use NSOutlineView to show DASchema object which by its nature is a good example of the tree-like data.
For more info check Here
Related
When I use a custom view as the cell of a view-based NSTableView, the custom view is somewhat below the table row. When I click on it, instead of affecting the elements (e.g. text field) custom view, the table row was selected (and highlighted). I have to reclick to select the text field.
- (NSView*)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSLog(#"We are creating views!");
NSTableCellView *newView;
newView = [tableView makeViewWithIdentifier:#"PostCell" owner:self];
NSTextField *newTextField = [[NSTextField alloc] init];
[newView addSubview:newTextField];
return newView;
}
When I disable the row selection according to NSTableView - Disable Row Selection, there was no selection.
- (BOOL)selectionShouldChangeInTableView:(NSTableView *)tableView {
return NO;
}
But I still cannot select directly the text field. What's worse, I cannot even select it using the mouse. Only tab on the keyboard works.
There seem to be something above it. But is it the "table column" shown in interface builder? Or something else?
How can I fix this?
Use a custom subclass of NSTableView and override -validateProposedFirstResponder:forEvent: to return YES.
See this blog entry from the Apple engineer who wrote the view-based table view code.
Make sure following code is present.
- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex {
return YES;
}
You may try logging the subviews Or you can check superviews of view.
This will help to understand view hierarchy.
Also on side note if one of the view's userInteraction is disable then it's subview's won't be able to receive the events. Please verify that all the views and it's subviews userInteraction is enable.
I hope this helps.
I'm trying to nest one NSTableView inside another view based tableview in my xib. When I do so, I get the following build error (when trying to compile the xib):
An instance of NSTableColumn with object ID jRp-dg-jOe did not archive its child (NSTableCellView) with an object of ID y8a-qz-ChK
Has anybody seen this, or know how to fix it?
I assume I could just create another xib for the NSTableCellView, and hook it up to the parent NSTableView using
NSNib *cellNib = [[NSNib alloc] initWithNibNamed:#"MyCellContainingAnNSTableView" bundle:nil];
[parentTable registerNib:cellNib forIdentifier:#"SomeIdentifier"];
but that's a little more annoying...
OK so it seems like nobody has any solutions for this...
After a long time looking, it seems that Interface Builder is just incapable of archiving nested NSTableViews. My solution was to create a new .xib containing an NSTableCellView and referencing that from the original NSTableView. Here's how to do it:
Create a new NSTableCellView in a separate .xib. Call it something like SchemeCell.xib
Add an NSTableViewDelegate to your code, and set it as the delegate for your main (parent) tableview.
Add in the method:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSNib *cellNib = [[NSNib alloc] initWithNibNamed:#"SchemeCell" bundle:nil];
[self.schemeTableView registerNib:cellNib forIdentifier:#"SchemeCell"];
});
return [self.schemeTableView makeViewWithIdentifier:#"SchemeCell" owner:self];
}
And away you go! Now the main disadvantage to this is that you can no longer use Interface Builder to hook up items from the SchemeCell to the parent NSTableView (since they are now both in separate .xibs, but it's not the end of the world, because you can do all of this within the code of course.
i try to get my head around view-based NSTableViews on OS X.
My Problem is that in the loaded view cell (NSView subclass) the subviews are not initialized when i try to assign the values in my delegate.
At the moment the correct count and the correct view is displayed, but i cannot access the subviews to assign the proper values.
What i have done so far:
Created the xib with the custom view cell in Interface Builder.
Created the custom class for the cell and assigned it in IB.
This work fine and i can see the properties in the Debugger. Correct class
and the properties are IBOulets that are wired to the correct fields.
I can see the call to:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
In my NSViewController:awakeFromNib i do:
// Make my view controller the delegate
_applicationTableView.delegate = self;
// set the correct datasource
_applicationTableView.dataSource = [NWDataHolder sharedInstance];
// register the nib with the custom cell
[_applicationTableView registerNib:cellNib forIdentifier:#"ApplicationListViewCell"];
In my - (NSView *)tableView:viewForTableColumn:row: i do:
NWApplicationListViewCell *cell = [_applicationTableView makeViewWithIdentifier:#"ApplicationListViewCell" owner:self];
The correct class and cell is returned and i have access to the properties.
The problem is, that subview is not initialized, the property is nil and the new value could not be set:
Log(#"Application Name Label %#", cell.applicationNameLabel); => nil
I`ve seen some hints that the subviews are initialized lazyly, but i cannot find a way to make the eager initialize.
Any suggestions what i'm doing wrong ?
Thanks,
Oliver
Fixed the problem. But i do not understand why this really happens.
I wired the fields in IB to the File Owner, but not to my ApplicationViewCell.
After wiring the property to both everything works fine.
Is there a way to get a reference to the view controller of my superview?
There were several instances that I needed this on the past couple of months, but didn't know how to do it. I mean, if I have a custom button on a custom cell, and I wish to get a reference of the table view controller that controls the cell I`m currently in, is there a code snippet for that? Or is it something that I should just solve it by using better design patterns?
Thanks!
Your button should preferably not know about its superviews view controller.
However, if your button really needs to message objects that it shouldn't know the details about, you can use delegation to send the messages you want to the buttons delegate.
Create a MyButtonDelegate protocol and define the methods that everyone that conforms to that protocol need to implement (the callback). You can have optional methods as well.
Then add a property on the button #property (weak) id<MyButtonDelegate> so that any class of any kind can be set as the delegate as long as it conforms to your protocol.
Now the view controller can implement the MyButtonDelegate protocol and set itself as the delegate. The parts of the code that require knowledge about the view controller should be implemented in the delegate method (or methods).
The view can now send the protocol messages to its delegate (without knowing who or what it is) and the delegate can to the appropriate thing for that button. This way the same button could be reused because it doesn't depend on where it is used.
When I asked this question I was thinking of, in a situation where I have custom cells with buttons on them, how can the TableViewController know which cell's button was tapped.
More recently, reading the book "iOS Recipes", I got the solution:
-(IBAction)cellButtonTapped:(id)sender
{
NSLog(#"%s", __FUNCTION__);
UIButton *button = sender;
//Convert the tapped point to the tableView coordinate system
CGPoint correctedPoint = [button convertPoint:button.bounds.origin toView:self.tableView];
//Get the cell at that point
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:correctedPoint];
NSLog(#"Button tapped in row %d", indexPath.row);
}
Another solution, a bit more fragile (though simpler) would be:
- (IBAction)cellButtonTapped:(id)sender
{
// Go get the enclosing cell manually
UITableViewCell *parentCell = [[sender superview] superview];
NSIndexPath *pathForButton = [self.tableView indexPathForCell:parentCell];
}
And the most reusable one would be to add this method to a category of UITableView
- (NSIndexPath *)prp_indexPathForRowContainingView:(UIView *)view
{
CGPoint correctedPoint = [view convertPoint:view.bounds.origin toView:self];
return [self indexPathForRowAtPoint:correctedPoint];
}
And then, on your UITableViewController class, just use this:
- (IBAction)cellButtonTapped:(id)sender
{
NSIndexPath *pathForButton = [self.tableView indexPathForRowContainingView:sender];
}
If you know which class is the superview of your view controller, you can just iterate through the subviews array and typecheck for your superclass.
eg.
UIView *view;
for(tempView in self.subviews) {
if([tempView isKindOfClass:[SuperViewController class] ])
{
// you got the reference, do waht you want
}
}
I have properly setup an NSOutlineView with its data source and it's working great.
It's actually a basic File explorer, showing the folder structure of a particular path (folders, subfolders, etc). The subfolders are loaded on demand (when a folder item is expanded only then are its contents loaded, for speed reasons).
What I want is to know HOW I could EASILY add support to what I already have for the ImageAndTextCell class, so that I can put e.g. a folder/file icon next to each entry...
Any help is appreciated. (Please don't point me to documentation; I've studied almost all of it; what I need is advice by some who has done it, so that I just ADD to my existing code; without having to rewrite from scratch or totally change the logic...)
Thanks
Here's *My Code * (I had some trouble formatting it for SO... so I posted it on Snippet.MX)
Did what suggested and all the outline view items' names are suddenly NOT appearing...
Documentation is your friend but I understand sometime is so huge.
BTW
You need to set the ImageAndTextCell for your outline view, you can do it on you window controller init or awakeFromNib method or directly (if needed) on your NSOutlineView subclass.
On my project I've a NSOutlineView subclass as shown below
// myOutlineView subclass
- (void)awakeFromNib {
self.imageCell = [[ImageAndTextCell alloc] init];
[self.imageCell setEditable: NO];
NSTableColumn* leftColumn = [[self tableColumns] objectAtIndex:0];
[leftColumn setDataCell: self.imageCell];
}
Then you need to implement willDisplayCell delegate method where you set the image for your specific column as shown below (folder icon, file txt icon, jpg icon).
On the code shown below I get it from my singleton VDIconUtils but you can simply return a NSImage
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item {
[cell setDrawsBackground:NO];
if ([[tableColumn identifier] isEqualToString:#"mycellname"]) {
[cell setImage:[[VDIconUtils sharedIconUtils] iconForFolderStatus:fs :16 :[outlineView isItemExpanded:item]]];
}
}