通过在Lion中拖动来重新排列表格行

时间:2011-12-31 02:11:29

标签: objective-c macos cocoa nstableview nsoutlineview

我在使用新的Lion功能重新排列应用中的行时遇到问题。我正在使用outlineView:pasteboardWriterForItem:存储行索引,以便稍后在验证/接受删除时可以访问它们。我创建了一个新的NSPasteboardItem来返回,并尝试将行号存储为:

[pbItem setData: [NSKeyedArchiver archivedDataWithRootObject: [NSNumber numberWithInteger: [fTableView rowForItem: item]]]
                                                     forType: TABLE_VIEW_DATA_TYPE];

TABLE_VIEW_DATA_TYPE是一个自定义字符串,用于区分拖动粘贴板中的自定义数据。我不会在拖动这些行之外使用它。

尝试拖动时,我会在控制台中收到:'TableViewDataType' is not a valid UTI string. Cannot set data for an invalid UTI.

当然我可以使用一些内置的UTI用于粘贴板,但它们都不适用(并且使用它们会导致拖动接受除行之外的拖动,它不应该)。是否有一些我缺少的东西,比如一种定义自定义UTI的方法,只是为了拖动(没有使它成为“真正的”UTI,因为我在内部拖动之外没有使用它,因此它不应该是公共的)。

感谢您的帮助!

3 个答案:

答案 0 :(得分:7)

我有类似的要求,除了我有一个对象网格,我想通过将所选对象拖动到新位置来重新排列。有几种方法可以执行此操作,包括创建自定义对象以及实施NSPasteboardWritingNSPasteboardReading协议(以及NSCoding协议,如果您要将数据读取为NSPasteboardReadingAsKeyedArchive) ,但这对于拖动仍然在应用程序内部的对象来说似乎有些过分。

我所做的是将NSPasteboardItem用作自定义UTI类型的包装器(它已经实现了NSPasteboardWritingNSPasteboardReading协议)。首先声明一个自定义UTI类型:

#define kUTIMyCustomType @“com.mycompany.MyApp.MyCustomType”

这需要以“com.domain.MyApp”格式定义,否则您将收到表单错误:“XXX不是有效的UTI字符串。无法为无效的UTI设置数据。“Apple在他们的文档中提到了这一点。

然后,您必须在将进行拖动的视图中注册此自定义UTI类型。这可以在运行时完成,并且不需要任何.plist添加。在视图的init方法中添加以下内容:

[self registerForDraggedTypes:[NSArray arrayWithObjects:(NSString *)kUTIMyCustomType, nil]];

现在,确保为此视图设置了委托,并且委托对象实现了所需的NSDraggingSourceNSDraggingDestination协议方法。这将允许您通过允许指定的控制器对象处理将数据放置在粘贴板上来避免破坏MVC设计模式,这可能涉及查询模型数据(即索引)。

具体来说,为了放置拖动粘贴板,拖动时要移动的对象的索引开始为索引数据的NSPasteboardItem包装器:

- (void) draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint
{
    NSPasteboard * pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
    [pboard clearContents];

    NSMutableArray * selectedIndexes = [NSMutableArray array];

    // Add NSString indexes for dragged items to pasteboard in NSPasteboardItem wrappers.
    for (MyModel * myModel in [self selectedObjects])
    {
        NSPasteboardItem * pasteboardItem = [[[NSPasteboardItem alloc] init] autorelease];
        [pasteboardItem setString:[NSString stringWithFormat:@"%@", [myModel index]]
                        forType:kUTIMyCustomType];
        [selectedIndexes addObject:pasteboardItem];
    }

    [pboard writeObjects:selectedIndexes];
}

当拖动操作完成时,要读取拖动的索引NSPasteboardItem数据:

- (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
{
    NSPasteboard * pasteboard = [sender draggingPasteboard];

    // Check for custom NSPasteboardItem's which wrap our custom object indexes.
    NSArray * classArray = [NSArray arrayWithObject:[NSPasteboardItem class]];
    NSArray * items = [pasteboard readObjectsForClasses:classArray options:[NSDictionary dictionary]];

    if (items == nil)
        return NO;

    // Convert array of NSPasteboardItem's with NSString index reps. to array of NSNumber indexes.
    NSMutableArray * indexes = [NSMutableArray array];
    for (NSPasteboardItem * item in items)
        [indexes addObject:[NSNumber numberWithInteger:[[item stringForType:kUTIMyCustomType] integerValue]]];

    //
    // Handle dragged indexes…
    //

    return YES;
}

答案 1 :(得分:6)

您可以使用的另一种技术是在侧面存储实例变量中拖动的对象的索引。除非您接受来自其他应用程序的项目,否则将所有内容放在粘贴板上并非绝对必要。

  1. 在awakeFromNib中,注册NSStringPboardType。
  2. 在... pasteboardWriterForRow中,返回[NSString string]。
  3. 在... draggingSession:willBegin ...中,将您的实例变量设置为您要跟踪的索引。
  4. 在validateDrop中,如果您的实例变量为nil或视图不是您的,则返回NSDragOperationNone。
  5. 在... draggingSession:结束...,nil out your instance variable。
  6. 希望有帮助...我正在使用该技术进行表格视图,但对于大纲视图它应该几乎相同。

答案 2 :(得分:2)

您应该创建一个符合NSPasteboardWriting协议的自定义对象,而不是使用vanilla NSPasteboardItem

在自定义对象中,您可以实现writableTypesForPasteboard:以返回粘贴板项目支持的自定义UTI列表。然后,实现pasteboardPropertyListForType:以在粘贴板请求时为适当的自定义UTI返回对象的plist表示。

您可以使用+propertyListWithData:options:format:error:的{​​{1}}方法从任意数据创建一个plist。

然后,您将覆盖表视图数据源中的NSPropertyListSerialization以返回自定义对象的实例。