4
문제는 드래그 앤 드롭이있는 NSOutlineView가 있는데 (드래그 앤 드롭 작업을 수행하는 코드의 포스트 하단 참조) 작동하지만 한 행을 드래그하면 드래그 된 행은 자식이되지만 다른 행에 드래그 한 행은 부모가되는 위치를 유지하고 두 행 중 하나를 삭제하면 둘 다 삭제됩니다. 무슨 뜻인지 보여주기 위해, 여기에 링크가 있습니다 - http://dvlp.me/6kya9NSOutlineView에서 드래그 앤 드롭 관련 문제
그리고 개요보기에서 드래그 앤 드롭 작업을 수행하는 코드는 다음과 같습니다.
헤더 파일.
@interface _NSControllerTreeProxy : NSObject
{
// opaque
}
//
// Number of objects at the root level.
//
- (unsigned int)count;
- (id)nodeAtIndexPath:(id)fp8;
- (id)objectAtIndexPath:(id)fp8;
@end
@interface _NSArrayControllerTreeNode : NSObject
{
// opaque
}
- (unsigned int)count;
- (id)observedObject;
- (id)parentNode;
- (id)nodeAtIndexPath:(id)fp8;
- (id)subnodeAtIndex:(unsigned int)fp8;
- (BOOL)isLeaf;
- (id)indexPath;
- (id)objectAtIndexPath:(id)fp8;
@end
// some more detailed reverse engineering is available here
// http://www.blueboxmoon.com/wiki/?page=Binding%20Tree
@interface DragController : NSObject
{
IBOutlet NSTreeController *groupTreeControl;
IBOutlet NSOutlineView *treeTable;
NSArray* dragType;
_NSArrayControllerTreeNode* draggedNode;
}
- (void) resortGroups:(NSManagedObjectContext*)objectContext forParent:(NSManagedObject*)parent;
- (NSArray*) getSubGroups:(NSManagedObjectContext*)objectContext forParent:(NSManagedObject*)parent;
- (BOOL) category:(NSManagedObject*)cat isSubCategoryOf:(NSManagedObject*) possibleSub;
@end
#define INTERVAL 10
구현 파일.
@implementation DragController
- (void)awakeFromNib {
dragType = [NSArray arrayWithObjects: @"factorialDragType", nil];
[ dragType retain ];
[ treeTable registerForDraggedTypes:dragType ];
NSSortDescriptor* sortDesc = [[NSSortDescriptor alloc] initWithKey:@"position" ascending:YES];
[groupTreeControl setSortDescriptors:[NSArray arrayWithObject: sortDesc]];
[ sortDesc release ];
}
//------------------------------------
#pragma mark NSOutlineView datasource methods -- see NSOutlineViewDataSource
//---------------------------------------------------------------------------
- (BOOL) outlineView : (NSOutlineView *) outlineView
writeItems : (NSArray*) items
toPasteboard : (NSPasteboard*) pboard {
[ pboard declareTypes:dragType owner:self ];
// items is an array of _NSArrayControllerTreeNode see http://theocacao.com/document.page/130 for more info
draggedNode = [ items objectAtIndex:0 ];
return YES;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index {
_NSArrayControllerTreeNode* parentNode = item;
_NSArrayControllerTreeNode* siblingNode;
_NSControllerTreeProxy* proxy = [ groupTreeControl arrangedObjects ];
NSManagedObject* draggedGroup = [ draggedNode observedObject ];
BOOL draggingDown = NO;
BOOL isRootLevelDrag = NO;
// ----------------------
// Setup comparison paths
// -------------------------
NSIndexPath* draggedPath = [ draggedNode indexPath ];
NSIndexPath* siblingPath = [ NSIndexPath indexPathWithIndex: index ];
if (parentNode == NULL) {
isRootLevelDrag = YES;
} else {
// A non-root drag - the index value is relative to this parent's children
siblingPath = [ [ parentNode indexPath ] indexPathByAddingIndex: index ];
}
// ----------------------
// Compare paths - modify sibling path for down drags, exit for redundant drags
// -----------------------------------------------------------------------------
switch ([ draggedPath compare:siblingPath]) {
case NSOrderedAscending: // reset path for down dragging
if (isRootLevelDrag) {
siblingPath = [ NSIndexPath indexPathWithIndex: index - 1];
} else {
siblingPath = [ [ parentNode indexPath ] indexPathByAddingIndex: index - 1 ];
}
draggingDown = YES;
break;
case NSOrderedSame:
return NO;
break;
}
siblingNode = [ proxy nodeAtIndexPath:siblingPath ];
// NSLog(@"returning early");
// return NO; // TODO robustify
// ------------------------------------------------------------
// SPECIAL CASE: Dragging to the bottom
// ------------------------------------------------------------
// - K - K - C - C
// - - U - - C OR - U - F
// - - C ====> - - F - F - K
// - - F - U - K - U
// ------------------------------------------------------------
if (isRootLevelDrag && siblingNode == NULL) {
draggingDown = YES;
siblingPath = [ NSIndexPath indexPathWithIndex: [ proxy count ] - 1 ];
siblingNode = [ proxy nodeAtIndexPath:siblingPath ] ;
}
// ------------------------------------------------------------
// Give the dragged item a position relative to it's new sibling
// ------------------------------------------------------------
NSManagedObject* sibling = [ siblingNode observedObject ];
NSNumber* bystanderPosition = [ sibling valueForKey:@"position"];
int newPos = (draggingDown ? [ bystanderPosition intValue ] + 1 : [ bystanderPosition intValue ] - 1);
[draggedGroup setValue:[ NSNumber numberWithInt:newPos ] forKey:@"position"];
// ----------------------------------------------------------------------------------------------
// Set the new parent for the dragged item, resort the position attributes and refresh the tree
// ----------------------------------------------------------------------------------------------
[ draggedGroup setValue:[ parentNode observedObject ] forKey:@"parent" ];
[ self resortGroups:[draggedGroup managedObjectContext] forParent:[ parentNode observedObject ] ];
[ groupTreeControl rearrangeObjects ];
return YES;
}
- (NSArray*) getSubGroups:(NSManagedObjectContext*)objectContext forParent:(NSManagedObject*)parent {
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"projects" inManagedObjectContext:objectContext];
[request setEntity:entity];
NSSortDescriptor* aSortDesc = [[NSSortDescriptor alloc] initWithKey:@"position" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject: aSortDesc] ];
[aSortDesc release];
NSPredicate* validationPredicate = [NSPredicate predicateWithFormat:@"parent == %@", parent ];
[ request setPredicate:validationPredicate ];
NSError *error = nil; // TODO - check the error bozo
return [objectContext executeFetchRequest:request error:&error];
}
- (void) resortGroups:(NSManagedObjectContext*)objectContext forParent:(NSManagedObject*)parent {
NSArray *array = [ self getSubGroups:objectContext forParent:parent ];
// Reset the indexes...
NSEnumerator *enumerator = [array objectEnumerator];
NSManagedObject* anObject;
int index = 0;
while (anObject = [enumerator nextObject]) {
// Multiply index by 10 to make dragging code easier to implement ;) ....
[anObject setValue:[ NSNumber numberWithInt:(index * INTERVAL) ] forKey:@"position"];
index++;
}
}
- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index {
_NSArrayControllerTreeNode* newParent = item;
// drags to the root are always acceptable
if (newParent == NULL) {
return NSDragOperationGeneric;
}
// Verify that we are not dragging a parent to one of it's ancestors
// causes a parent loop where a group of nodes point to each other and disappear
// from the control
NSManagedObject* dragged = [ draggedNode observedObject ];
NSManagedObject* newP = [ newParent observedObject ];
if ([ self category:dragged isSubCategoryOf:newP ]) {
return NO;
}
return NSDragOperationGeneric;
}
- (BOOL) category:(NSManagedObject*)cat isSubCategoryOf:(NSManagedObject*) possibleSub {
// Depends on your interpretation of subCategory ....
if (cat == possibleSub) { return YES; }
NSManagedObject* possSubParent = [possibleSub valueForKey:@"parent"];
if (possSubParent == NULL) { return NO; }
while (possSubParent != NULL) {
if (possSubParent == cat) { return YES; }
// move up the tree
possSubParent = [possSubParent valueForKey:@"parent"];
}
return NO;
}
// This method gets called by the framework but the values from bindings are used instead
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
return NULL;
}
/*
The following are implemented as stubs because they are required when
implementing an NSOutlineViewDataSource. Because we use bindings on the
table column these methods are never called. The NSLog statements have been
included to prove that these methods are not called.
*/
- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
NSLog(@"numberOfChildrenOfItem");
return 1;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
NSLog(@"isItemExpandable");
return NO;
}
- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item {
NSLog(@"child of Item");
return NULL;
}
@end
아, 그걸 어떻게하는거야? 나는이 문제를 스스로 해결해 냈으며, 이제는 수정이 쉽다는 것을 당혹 스럽다. 고마워, 그래! –