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

liv*_*124 6 macos cocoa objective-c nsoutlineview nstableview

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

[pbItem setData: [NSKeyedArchiver archivedDataWithRootObject: [NSNumber numberWithInteger: [fTableView rowForItem: item]]]
                                                     forType: TABLE_VIEW_DATA_TYPE];
Run Code Online (Sandbox Code Playgroud)

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

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

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

谢谢你的帮助!

Dal*_*zio 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]];
Run Code Online (Sandbox Code Playgroud)

现在,确保为此视图设置了委托,委托对象实现了必需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];
}
Run Code Online (Sandbox Code Playgroud)

当拖动操作完成时,要读取拖动的索引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;
}
Run Code Online (Sandbox Code Playgroud)


Rya*_*yan 6

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

  1. 在awakeFromNib中,注册NSStringPboardType.
  2. 在... pasteboardWriterForRow中,返回[NSString string].
  3. 在... draggingSession:willBegin ...中,将实例变量设置为要跟踪的索引.
  4. 在validateDrop中,如果您的实例变量为nil或视图不是您的,则返回NSDragOperationNone.
  5. 在... draggingSession:结束...,nil你的实例变量.

希望有帮助...我正在使用该技术进行表格视图,但它对于大纲视图应该几乎相同.


Rob*_*ger 3

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

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

+propertyListWithData:options:format:error:您可以使用的方法从任意数据创建 plist NSPropertyListSerialization

然后,您可以覆盖tableView:pasteboardWriterForRow:表视图数据源以返回自定义对象的实例。

  • 我不确定规则到底是什么,但 UTI 可能需要符合标准格式才能有效,第一个组件需要与您的应用程序的域相匹配。例如“com.mydomain.myapp.mydatatype”。现在您可能已经确定自己需要什么了! (3认同)