NSOutlineView 拖放(在项目旁边)不适用于复杂项目
NSOutlineView drag&drop (next to an item) doesn't work for complex items
我的基本示例适用于由标准 ObjC classes 表示的项目。
请参阅下面示例中 self.list
的初始化:
- (void) updateViews;
{
NSDictionary *firstParent = [NSDictionary dictionaryWithObjectsAndKeys:@"Foo",@"parent",[NSArray arrayWithObjects:@"Foox",@"Fooz", nil],@"children", nil];
NSDictionary *secondParent = [NSDictionary dictionaryWithObjectsAndKeys:@"Bar",@"parent",[NSArray arrayWithObjects:@"Barx",@"Barz", nil],@"children", nil];
self.list = [NSArray arrayWithObjects:firstParent,secondParent, nil];
self.outlineView.delegate = self;
self.outlineView.dataSource = self;
// Enable Drag and Drop
[self.outlineView registerForDraggedTypes:@[LOCAL_REORDER_PASTEBOARD_TYPE]];
}
#pragma mark - NSOutlineViewDelegate
- (BOOL) outlineView:(NSOutlineView*)outlineView isItemExpandable:(id)item
{
if ([item isKindOfClass:[NSDictionary class]]) {
return YES;
}
else {
return NO;
}
}
- (NSInteger) outlineView:(NSOutlineView*)outlineView numberOfChildrenOfItem:(id)item
{
if (item == nil) { //item is nil when the outline view wants to inquire for root level items
return [self.list count];
}
if ([item isKindOfClass:[NSDictionary class]]) {
return [[item objectForKey:@"children"] count];
}
return 0;
}
- (id) outlineView:(NSOutlineView*)outlineView child:(NSInteger)index ofItem:(id)item
{
if (item == nil) { //item is nil when the outline view wants to inquire for root level items
return [self.list objectAtIndex:index];
}
if ([item isKindOfClass:[NSDictionary class]]) {
return [[item objectForKey:@"children"] objectAtIndex:index];
}
return nil;
}
- (NSView*) outlineView:(NSOutlineView*)outlineView viewForTableColumn:(NSTableColumn*)tableColumn item:(id)item
{
NSTableCellView *result = [outlineView makeViewWithIdentifier:@"DataCell" owner:self];
NSString *txt = nil;
if ([item isKindOfClass:[NSDictionary class]]) {
txt = [item objectForKey:@"parent"];//[NSString stringWithFormat:@"%i kids", [[item objectForKey:@"children"] count]];
}
else {
txt = str(item);
}
result.textField.stringValue = txt;
return result;
}
- (BOOL) outlineView:(NSOutlineView*)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pasteboard
{
[pasteboard declareTypes:[NSArray arrayWithObject:LOCAL_REORDER_PASTEBOARD_TYPE] owner:self];
[pasteboard setData:[@"just a test - not yet implemented" dataUsingEncoding:NSUTF8StringEncoding] 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;
}
拖放按预期工作。
一旦我将 child:(NSInteger)index ofItem:(id)item
方法修改为 return 我自己的复杂项目 class,特别是 TreeItem
然后拖放只允许:
- 掉落物品
- 放在父项目旁边
但是第一张图片所示的叶项目旁边的拖放是不可能的。知道为什么吗?
我还要添加无法正常工作的复杂项目的示例代码。
两种情况的情节提要和拖放代码都相同。
#define LOCAL_REORDER_PASTEBOARD_TYPE @"LOCAL_REORDER_PASTEBOARD_TYPE"
#define str(...) [@[__VA_ARGS__] componentsJoinedByString:@""]
@interface TreeItem : NSObject
@property (nonatomic) NSArray<TreeItem*> *children;
@property (nonatomic) NSString *name;
+ (nonnull instancetype) itemWithName:(nonnull NSString*)name;
@end
@implementation TreeItem
+ (nonnull instancetype) itemWithName:(nonnull NSString*)name;
{
TreeItem *obj = [[self alloc] init];
obj.name = name;
return obj;
}
- (NSArray<TreeItem*>*) children;
{
return self.name.length > 3 ? nil : @[
[TreeItem itemWithName:str(self.name, @"x")],
[TreeItem itemWithName:str(self.name, @"z")],
];
}
@end
@interface SampleVC ()
@property (weak) IBOutlet NSOutlineView *outlineView;
@property (strong) NSArray<TreeItem*> *treeList;
@end
@implementation SampleVC
- (void) updateViews;
{
self.treeList = @[
[TreeItem itemWithName:@"Foo"],
[TreeItem itemWithName:@"Bar"],
];
self.outlineView.delegate = self;
self.outlineView.dataSource = self;
[self.outlineView reloadData];
[self.outlineView registerForDraggedTypes:@[LOCAL_REORDER_PASTEBOARD_TYPE]];
}
- (BOOL) outlineView:(NSOutlineView*)outlineView isItemExpandable:(id)item
{
return item == nil ? YES : ((TreeItem*)item).children != nil;
}
- (NSInteger) outlineView:(NSOutlineView*)outlineView numberOfChildrenOfItem:(id)item
{
return item == nil ? self.treeList.count : (((TreeItem*)item).children != nil ? ((TreeItem*)item).children.count : 0);
}
- (id) outlineView:(NSOutlineView*)outlineView child:(NSInteger)index ofItem:(id)item
{
return item == nil ? [self.treeList objectAtIndex:index] : ((TreeItem*)item).children[index];
}
- (NSView*) outlineView:(NSOutlineView*)outlineView viewForTableColumn:(NSTableColumn*)tableColumn item:(id)item
{
NSTableCellView *result = [outlineView makeViewWithIdentifier:@"DataCell" owner:self];
result.textField.stringValue = ((TreeItem*)item).name;
return result;
}
@end
大纲视图找不到子项所在的行,并在视图顶部绘制了放置指示器。 NSOutlineView
的 rowForItem:
无法工作,如果 TreeItem
returns 每次调用 - (NSArray<TreeItem*>*) children;
新的 TreeItem
数组。
我的基本示例适用于由标准 ObjC classes 表示的项目。
请参阅下面示例中 self.list
的初始化:
- (void) updateViews;
{
NSDictionary *firstParent = [NSDictionary dictionaryWithObjectsAndKeys:@"Foo",@"parent",[NSArray arrayWithObjects:@"Foox",@"Fooz", nil],@"children", nil];
NSDictionary *secondParent = [NSDictionary dictionaryWithObjectsAndKeys:@"Bar",@"parent",[NSArray arrayWithObjects:@"Barx",@"Barz", nil],@"children", nil];
self.list = [NSArray arrayWithObjects:firstParent,secondParent, nil];
self.outlineView.delegate = self;
self.outlineView.dataSource = self;
// Enable Drag and Drop
[self.outlineView registerForDraggedTypes:@[LOCAL_REORDER_PASTEBOARD_TYPE]];
}
#pragma mark - NSOutlineViewDelegate
- (BOOL) outlineView:(NSOutlineView*)outlineView isItemExpandable:(id)item
{
if ([item isKindOfClass:[NSDictionary class]]) {
return YES;
}
else {
return NO;
}
}
- (NSInteger) outlineView:(NSOutlineView*)outlineView numberOfChildrenOfItem:(id)item
{
if (item == nil) { //item is nil when the outline view wants to inquire for root level items
return [self.list count];
}
if ([item isKindOfClass:[NSDictionary class]]) {
return [[item objectForKey:@"children"] count];
}
return 0;
}
- (id) outlineView:(NSOutlineView*)outlineView child:(NSInteger)index ofItem:(id)item
{
if (item == nil) { //item is nil when the outline view wants to inquire for root level items
return [self.list objectAtIndex:index];
}
if ([item isKindOfClass:[NSDictionary class]]) {
return [[item objectForKey:@"children"] objectAtIndex:index];
}
return nil;
}
- (NSView*) outlineView:(NSOutlineView*)outlineView viewForTableColumn:(NSTableColumn*)tableColumn item:(id)item
{
NSTableCellView *result = [outlineView makeViewWithIdentifier:@"DataCell" owner:self];
NSString *txt = nil;
if ([item isKindOfClass:[NSDictionary class]]) {
txt = [item objectForKey:@"parent"];//[NSString stringWithFormat:@"%i kids", [[item objectForKey:@"children"] count]];
}
else {
txt = str(item);
}
result.textField.stringValue = txt;
return result;
}
- (BOOL) outlineView:(NSOutlineView*)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pasteboard
{
[pasteboard declareTypes:[NSArray arrayWithObject:LOCAL_REORDER_PASTEBOARD_TYPE] owner:self];
[pasteboard setData:[@"just a test - not yet implemented" dataUsingEncoding:NSUTF8StringEncoding] 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;
}
拖放按预期工作。
一旦我将 child:(NSInteger)index ofItem:(id)item
方法修改为 return 我自己的复杂项目 class,特别是 TreeItem
然后拖放只允许:
- 掉落物品
- 放在父项目旁边
但是第一张图片所示的叶项目旁边的拖放是不可能的。知道为什么吗?
我还要添加无法正常工作的复杂项目的示例代码。 两种情况的情节提要和拖放代码都相同。
#define LOCAL_REORDER_PASTEBOARD_TYPE @"LOCAL_REORDER_PASTEBOARD_TYPE"
#define str(...) [@[__VA_ARGS__] componentsJoinedByString:@""]
@interface TreeItem : NSObject
@property (nonatomic) NSArray<TreeItem*> *children;
@property (nonatomic) NSString *name;
+ (nonnull instancetype) itemWithName:(nonnull NSString*)name;
@end
@implementation TreeItem
+ (nonnull instancetype) itemWithName:(nonnull NSString*)name;
{
TreeItem *obj = [[self alloc] init];
obj.name = name;
return obj;
}
- (NSArray<TreeItem*>*) children;
{
return self.name.length > 3 ? nil : @[
[TreeItem itemWithName:str(self.name, @"x")],
[TreeItem itemWithName:str(self.name, @"z")],
];
}
@end
@interface SampleVC ()
@property (weak) IBOutlet NSOutlineView *outlineView;
@property (strong) NSArray<TreeItem*> *treeList;
@end
@implementation SampleVC
- (void) updateViews;
{
self.treeList = @[
[TreeItem itemWithName:@"Foo"],
[TreeItem itemWithName:@"Bar"],
];
self.outlineView.delegate = self;
self.outlineView.dataSource = self;
[self.outlineView reloadData];
[self.outlineView registerForDraggedTypes:@[LOCAL_REORDER_PASTEBOARD_TYPE]];
}
- (BOOL) outlineView:(NSOutlineView*)outlineView isItemExpandable:(id)item
{
return item == nil ? YES : ((TreeItem*)item).children != nil;
}
- (NSInteger) outlineView:(NSOutlineView*)outlineView numberOfChildrenOfItem:(id)item
{
return item == nil ? self.treeList.count : (((TreeItem*)item).children != nil ? ((TreeItem*)item).children.count : 0);
}
- (id) outlineView:(NSOutlineView*)outlineView child:(NSInteger)index ofItem:(id)item
{
return item == nil ? [self.treeList objectAtIndex:index] : ((TreeItem*)item).children[index];
}
- (NSView*) outlineView:(NSOutlineView*)outlineView viewForTableColumn:(NSTableColumn*)tableColumn item:(id)item
{
NSTableCellView *result = [outlineView makeViewWithIdentifier:@"DataCell" owner:self];
result.textField.stringValue = ((TreeItem*)item).name;
return result;
}
@end
大纲视图找不到子项所在的行,并在视图顶部绘制了放置指示器。 NSOutlineView
的 rowForItem:
无法工作,如果 TreeItem
returns 每次调用 - (NSArray<TreeItem*>*) children;
新的 TreeItem
数组。