Created
April 24, 2019 20:44
-
-
Save tylerhall/12e597779a0c0504dc3777335daa543f to your computer and use it in GitHub Desktop.
NSOutlineView Drag and Drop with Core Data
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- (void)outlineViewItemDidCollapse:(NSNotification *)notification { | |
VirtualHost *vh = [[[notification userInfo] valueForKey:@"NSObject"] representedObject]; | |
vh.isExpandedValue = NO; | |
} | |
- (void)outlineViewItemDidExpand:(NSNotification *)notification { | |
VirtualHost *vh = [[[notification userInfo] valueForKey:@"NSObject"] representedObject]; | |
vh.isExpandedValue = YES; | |
} | |
</code></pre> | |
<p>And then subclass your NSOutlineView’s <code>reloadData</code> method to be…</p> | |
<pre><code>- (void)reloadData { | |
[super reloadData]; | |
for(NSUInteger row = 0; row < [self numberOfRows]; row++) { | |
NSTreeNode *node = [self itemAtRow:row]; | |
VirtualHost *vh = [node representedObject]; | |
if(vh.isExpandedValue) { | |
[self expandItem:node]; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface OutlineViewController : NSObject <NSOutlineViewDataSource, NSOutlineViewDelegate> { | |
IBOutlet NSTreeController *_treeController; | |
IBOutlet NSOutlineView *_outlineView; | |
NSArray *_dragType; | |
NSTreeNode *_draggedNode; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- (void)awakeFromNib { | |
// Make the outline view draggable... | |
_dragType = [NSArray arrayWithObject:@"myDragType"]; | |
[_outlineView registerForDraggedTypes:_dragType]; | |
// Sort the outline view's item using the appropriate Core Data property on our entity... | |
NSSortDescriptor *sortDesc = [[NSSortDescriptor alloc] initWithKey:@"sortOrder" ascending:YES]; | |
[_treeController setSortDescriptors:@[sortDesc]]; | |
} | |
// Save a reference to what's being dragged when we begin dragging... | |
- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pasteboard { | |
[pasteboard declareTypes:_dragType owner:self]; | |
_draggedNode = [items objectAtIndex:0]; | |
return YES; | |
} | |
// Here's what we do when the item is actually dropped... | |
- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id<NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index { | |
VirtualHost *parent = [item representedObject]; | |
VirtualHost *vh = [_draggedNode representedObject]; | |
[vh setValue:parent forKey:@"parent"]; | |
// If we dropped a new top-level item... | |
if(parent == NULL) { | |
// Get an arrary of all the other top-level items... | |
// (We could have just done a Core Data fetch, but this works, too.) | |
NSMutableArray *rootLevelItems = [NSMutableArray array]; | |
for(NSUInteger i = 0; i < [_outlineView numberOfRows]; i++) { | |
VirtualHost *item = [[_outlineView itemAtRow:i] representedObject]; | |
if(item.parent == NULL) { | |
[rootLevelItems addObject:item]; | |
} | |
} | |
// Order them appropriately... | |
for(NSUInteger i = 0; i < [rootLevelItems count]; i++) { | |
VirtualHost *item = [rootLevelItems objectAtIndex:i]; | |
item.sortOrderValue = (i + 1) * 2; | |
} | |
vh.sortOrderValue = (index + 1) * 2 - 1; | |
} else { // We dropped within another parent item... | |
// Get a sorted array of the other children... | |
NSSortDescriptor *sorter = [NSSortDescriptor sortDescriptorWithKey:@"sortOrder" ascending:YES]; | |
NSArray *children = [parent.children sortedArrayUsingDescriptors:@[ sorter ]]; | |
// Order them appropriately... | |
for(NSUInteger i = 0; i < [children count]; i++) { | |
VirtualHost *item = [children objectAtIndex:i]; | |
item.sortOrderValue = (i + 1) * 2; | |
} | |
vh.sortOrderValue = (index + 1) * 2 - 1; | |
} | |
// Tell the tree controller to resort the items based on the order we just put them in | |
[_treeController rearrangeObjects]; | |
return YES; | |
} | |
// Decide which type of drag operations are allowed... | |
- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id<NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index { | |
// Drags to root are always ok... | |
if([item representedObject] == NULL) { | |
return NSDragOperationGeneric; | |
} | |
// Don't allow drags to non-groups... | |
VirtualHost *vh = [item representedObject]; | |
if(!vh.isFolderValue) { | |
return NSDragOperationNone; | |
} | |
// Don't allow dragging groups into other groups. | |
// You can remove this if you're ok with nested groups. | |
VirtualHost *dragged = [_draggedNode representedObject]; | |
if(dragged.isFolderValue && vh.isFolderValue) { | |
return NSDragOperationNone; | |
} | |
// Verify that we are not dragging a parent to one of its ancestors | |
NSManagedObject *newP = [item representedObject]; | |
if([self item:dragged isSubItemOf:newP]) { | |
return NSDragOperationNone; | |
} | |
return NSDragOperationGeneric; | |
} | |
- (BOOL)item:(NSManagedObject *)cat isSubItemOf:(NSManagedObject *)possibleSub { | |
if(cat == possibleSub) { | |
return YES; | |
} | |
NSManagedObject *possibleSubParent = [possibleSub valueForKey:@"parent"]; | |
if(possibleSubParent == NULL) { | |
return NO; | |
} | |
while(possibleSubParent != NULL) { | |
if(possibleSubParent == cat) { | |
return YES; | |
} | |
possibleSubParent = [possibleSubParent valueForKey:@"parent"]; | |
} | |
return NO; | |
} | |
// These are required, but never actually called because we're using bindings... | |
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item { | |
return 1; | |
} | |
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item { | |
return NULL; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment