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.
Related
I have a view which (among other elements) holds a cell based NSTableView. I made the ViewController the delegate of the table. The view controller is created programmatically and added to an NSStatusItem.
I implemented:
- (BOOL)tableView:(NSTableView *)tableView shouldShowCellExpansionForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
return YES;
}
- (NSString *)tableView:(NSTableView *)aTableView toolTipForCell:(NSCell *)aCell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)row mouseLocation:(NSPoint)mouseLocation {
return #"A tooltip";
}
- (BOOL)tableView:(NSTableView *)tableView shouldTrackCell:(NSCell *)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
return YES;
}
- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex {
NSLog(#"Display");
}
But when I run my code the only one of these methods that gets called is:
- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex;
Any idea what might be wrong?
My real objective is to dinamycally define the tooltips for my cells.
I've just needed to solve this problem for my app's development.
These delegate methods you mentioned above are only for the expansion tooltip of a text cell, that shows full text when truncated.
A possible solution is to add an observer of NSViewFrameDidChangeNotification in which the tooltip rectangles are rearranged using addToolTipRect:owner:userData:. The code is something like:
NSTableView *tableView; // the target tableview
- (void)tableViewSizeDidChange:(NSNotification *)theNotification {
[tableView removeAllToolTips];
for (NSInteger row = 0; row < tableView.numberOfRows; row ++) {
NSRect rowRect = [tableView rectOfRow:row];
for (NSInteger col = 0; col < tableView.numberOfColumns; col ++) {
NSRect colRect = [tableView rectOfColumn:col];
NSRect cellRect = NSIntersectionRect(rowRect, colRect);
[tableView addToolTipRect:cellRect owner:
/* Tooltip string for cell at row and col */ userData:NULL];
}}
}
- (void)windowDidLoad {
...
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(tableViewSizeDidChange:)
name:NSViewFrameDidChangeNotification object:tableView];
...
You may also need to call tableViewSizeDidChange: when a table column was moved or resized.
I've seen some threads with a similar question to this (not exactly the same) but the solutions there didn't solve my problem. I've create a NSTableView with a couple clickable elements in each TableCellView -- a TextField, a DatePicker and a Button -- and I need to be able to click into each of these without first selecting the row they are in. I achieved this by using setSelectionHighlightStyle: NSTableViewSelectionHighlightStyleNone However, I still need to know the index of the row that is being edited, because I need to pass the edits into a model object associated with each row. I tried writing the following, which was the suggestion of some of the other threads I mentioned, but it didn't do anything.
- (BOOL)tableView:(NSTableView *)tableView shouldTrackCell:(NSCell *)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
return YES;
}
I'm not 100% sure if that's the wrong method to use for this, or if I'm just implementing it incorrectly. Any tips on how I could go about this would be awesome.
Edit:
Here's my attempt at implementing Steve Waddicor's suggestions:
Making the ViewController the NSTableViewDataSource and set the TableCellView as the delegates for the objects (code is in MyViewController.m):
- (void)awakeFromNib{
[self.tableView setDataSource:self];
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
CustomTableCellView *view = [self.tableView makeViewWithIdentifier:#"MyIdentifier" owner:self];
[view.textField setDelegate:view];
[view.datePicker setDelegate:view];
return view;
}
//-----------------------
//-----------------------
//----------------------- NSTableViewDataSource Protocol Requirements:
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
{
return [self.tableView numberOfRows];
}
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
return [self.tableView rowViewAtRow:rowIndex makeIfNecessary:NO];
}
and in CustomTableCellView, this is the delegate method which isn't being called when I click on the TextField, which leads me to believe that the delegates aren't being set for the objects correctly:
- (void)mouseDown:(NSEvent*) theEvent{
NSLog(#"TEST");
}
You could set the your tableCellView as the delegate of the controls. From there you could
NSTableView* tableView = self.superview;
NSUInteger row = [tableView rowForView:self];
Edit
To make a delegate. In your tableView dataSource you have a method something like this:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
MyTableCellView* view = [outlineView makeViewWithIdentifier:#"MyIdentifier" owner:self];
[view.textField setDelegate:view];
[view.datePicker setDelegate:view];
[view.button setDelegate:view];
return view;
}
try this delegate
- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex
{
return YES;
}
I've got NSOutlineView that, at the moment, only lists children of the root item. Everything works except when I try to rearrange items using drag & drop. The line with the circle in front of it always stays above the top row and doesn't follow my items when they need to be dragged between other items. However, I still get the index positions properly so I can still rearrange them in the datasource properly. I don't want to rearrange items by dragging them onto each other, I only want to rearrange the items off the root level (like the VLC playlist on the Mac).
Here are my four required methods:
//queue is a C-based data structure
-(NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
if(item == nil){
return queue.count; //each list item
}else{
return 0; //no children of each list item allowed
}
}
-(id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{
if(item != nil) return nil;
return [[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithLong:index],#"index",
[NSString stringWithFormat:#"%s",queue.item[index].filepath],#"filePath",
nil] copy];
}
-(BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
if(item == nil){
return YES;
}else{
return NO;
}
}
-(id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
if(item == nil) return nil;
return [item objectForKey:[tableColumn identifier]];
}
And my drag methods:
-(BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pasteboard
{
[pasteboard declareTypes:[NSArray arrayWithObject:LOCAL_REORDER_PASTEBOARD_TYPE] owner:self];
[pasteboard setData:[NSData data] forType:LOCAL_REORDER_PASTEBOARD_TYPE];
return YES;
}
-(NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
{
NSUInteger op = NSDragOperationNone;
if(index != NSOutlineViewDropOnItemIndex){
op = NSDragOperationMove;
}
return op;
}
I'm kind of confused about what desired behavior you want. Does this sample project achieve the desired drag and drop highlighting behavior (it shows the o–––––– indicator between each row, which is how VLC appears to work here for me):
http://www.markdouma.com/developer/NSOutlineViewFinagler.zip
(Note that it doesn't include code yet to actually carry through the reordering of the items).
If not, can you describe again what it currently does now and what you want it to do?
Also, you didn't include code for it here, but did you make sure to register the outline view for the LOCAL_REORDER_PASTEBOARD_TYPE drag type? I would do it in awakeFromNib:
- (void)awakeFromNib {
[self.outlineView registerForDraggedTypes:#[LOCAL_REORDER_PASTEBOARD_TYPE]];
}
That's necessary for the outline view to allow itself to be a drag destination.
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 am implementing NSOutlineView and having implemented following method,
-(void) initOutlineView{
pMyOutlineView = [[[MyUICustomOutlineView alloc] initWithFrame:clipViewBounds]
autorelease];
NSTableColumn* firstColumn = [[[NSTableColumn alloc] initWithIdentifier:#"firstColumn"] autorelease];
[firstColumn setWidth:25];
[pMyOutlineView addTableColumn:firstColumn];
NSTableColumn* secondColumn = [[[NSTableColumn alloc] initWithIdentifier:#"secondColumn"] autorelease];
NSTextFieldCell *pCell = [[NSTextFieldCell alloc]init];
[secondColumn setDataCell:pCell];
[secondColumn setWidth:180];
[pMyOutlineView addTableColumn:secondColumn];
[pMyOutlineView setRowHeight:30];
pNodeArray = [[NSMutableArray alloc]initWithCapacity:10];
PointerNode *pNode = pointerList.getHead();
int idx =0;
void *ptr = nil;
while ( contact ) {
[pNodeArray insertObject:[NSValue valueWithPointer:(void *)pNode]
atIndex:idx];
pNode = pNode->getNext();
idx++;
}
[pMyOutlineView setDataSource:self];
// this is to tell myCustomOutlineView to delegate Menu and Mouse event to
// this interface
[pMyOutlineView setDataDelegate:self];
[scrollView setDocumentView:pMyOutlineView];
[pMyOutlineView setDelegate:self];
}
And Implemented following delegate Method
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
// Here it will come for the top level element
if(item==nil)
return pointerList.size();
/*
If its not NULL then it must be some child element
*/
if([item isKindOfClass:[NSValue class]])
{
// yes it may have children
PointerNode *pNode = (PointerNode *)[item pointerValue];
if(pNode->hasChildren()){
return pNode->getNoOfChild();
}
}
return 0; // means this element not going to have any children
}
Some other method
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
and get data
i am following some blogs , tutorial,
the problem what i am facing is as par documentation it should hit in numberOfChildrenOfItem for each element where we need to calculate and send the No of child for that item,
but the problem what i am facing, its coming to above function only once and for that too item is nil, i.e. its not coming for other element,
Am i missing any method that needs to be delegate
other method which i override is as below,
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)aTableColumn byItem:(id)item
- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
Please tell me , which method i am missing
In outlineView:numberOfChildrenOfItem:, you need to test if item is nil and, if so, return the number of children of the top level object. Quoting the documentation,
If item is nil, this method should return the number of children for the top-level item.
If you’re returning 0 when item is nil, the outline view considers that there are no children for the top level item, hence it won’t send this message again to your data source object.
#Rohan did you implemented the datasource methods or not.
Check out the documentation for the NSOutlineView
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSOutlineView_Class/Reference/Reference.html