Skip to content

Instantly share code, notes, and snippets.

@tylerhall
Created April 24, 2019 20:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tylerhall/12e597779a0c0504dc3777335daa543f to your computer and use it in GitHub Desktop.
Save tylerhall/12e597779a0c0504dc3777335daa543f to your computer and use it in GitHub Desktop.
NSOutlineView Drag and Drop with Core Data
- (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&#8217;s <code>reloadData</code> method to be&#8230;</p>
<pre><code>- (void)reloadData {
[super reloadData];
for(NSUInteger row = 0; row &lt; [self numberOfRows]; row++) {
NSTreeNode *node = [self itemAtRow:row];
VirtualHost *vh = [node representedObject];
if(vh.isExpandedValue) {
[self expandItem:node];
}
}
}
interface OutlineViewController : NSObject &lt;NSOutlineViewDataSource, NSOutlineViewDelegate&gt; {
IBOutlet NSTreeController *_treeController;
IBOutlet NSOutlineView *_outlineView;
NSArray *_dragType;
NSTreeNode *_draggedNode;
}
- (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&lt;NSDraggingInfo&gt;)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 &lt; [_outlineView numberOfRows]; i++) {
VirtualHost *item = [[_outlineView itemAtRow:i] representedObject];
if(item.parent == NULL) {
[rootLevelItems addObject:item];
}
}
// Order them appropriately...
for(NSUInteger i = 0; i &lt; [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 &lt; [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&lt;NSDraggingInfo&gt;)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 &amp;&amp; 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