перестановка строк таблицы путем перетаскивания Lion

У меня возникли проблемы с использованием новой функции 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, поскольку я не использую его вне внутреннего перетаскивания, поэтому он не должен быть общедоступным).

Спасибо за любую помощь!


person livings124    schedule 31.12.2011    source источник


Ответы (3)


У меня были аналогичные требования, за исключением того, что у меня была сетка объектов, которые я хотел изменить, перетаскивая выбранные объекты в новое место. Есть несколько способов сделать это, включая создание пользовательского объекта и реализацию протоколов NSPasteboardWriting и NSPasteboardReading (и протоколов NSCoding, если вы будете считывать данные как NSPasteboardReadingAsKeyedArchive), но это кажется излишним для перетаскивания объектов, которые остаются внутренними для заявление.

То, что я сделал, включает использование NSPasteboardItem в качестве оболочки с пользовательским типом UTI (он уже реализует протоколы NSPasteboardWriting и NSPasteboardReading). Сначала объявите пользовательский тип UTI:

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

Это необходимо определить в формате «com.domain.MyApp», иначе вы получите ошибки вида: «XXX не является допустимой строкой UTI. Невозможно установить данные для недействительного UTI». Apple упоминает об этом в своей документации.

Затем вы должны зарегистрировать этот пользовательский тип UTI в представлении, в котором будет происходить перетаскивание. Это можно сделать во время выполнения и не требует каких-либо дополнений .plist. В методе инициализации вашего представления добавьте следующее:

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

Теперь убедитесь, что делегат установлен для этого представления, а объект делегата реализует необходимые методы протокола NSDraggingSource и NSDraggingDestination. Это позволит вам не нарушать шаблон проектирования 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;
}
person Dalmazio    schedule 24.12.2013

Другой метод, который вы можете использовать, — просто сохранить индексы объектов, которые вы перетаскиваете, в переменной экземпляра сбоку. Помещать все на монтажный стол не обязательно, если только вы не принимаете элементы из другого приложения или наоборот.

  1. В awakeFromNib зарегистрируйтесь для NSStringPboardType.
  2. В … pasteboardWriterForRow верните [строка NSString].
  3. В … draggingSession:willBegin… установите переменную экземпляра на индексы, которые вы хотите отслеживать.
  4. В validateDrop верните NSDragOperationNone, если ваша переменная экземпляра равна нулю или представление не принадлежит вам.
  5. В … draggingSession: Ended … обнулите вашу переменную экземпляра.

Надеюсь, это поможет... Я использую эту технику для табличного представления, но она должна быть практически идентична для представления структуры.

person Ryan    schedule 08.03.2012

Вместо использования стандартного NSPasteboardItem следует создать собственный объект, соответствующий NSPasteboardWriting протокол.

В своем пользовательском объекте вы можете реализовать writableTypesForPasteboard: для возврата списка пользовательских UTI, которые поддерживает ваш элемент монтажного стола. Затем вы реализуете pasteboardPropertyListForType: для возврата plist-представления вашего объекта для соответствующего пользовательского UTI, когда монтажный стол запрашивает его.

Вы можете создать plist из произвольных данных, используя метод +propertyListWithData:options:format:error: из NSPropertyListSerialization.

Затем вы должны переопределить tableView:pasteboardWriterForRow: в источнике данных табличного представления, чтобы вернуть экземпляр вашего пользовательского объекта.

person Rob Keniger    schedule 31.12.2011
comment
Спасибо, я ценю ответ. Я до сих пор очень удивлен, что так сложно просто отслеживать переставляемые строки. - person livings124; 31.12.2011
comment
Я реализовал объект, соответствующий NSPasteboardWriting, который возвращает мой пользовательский UTI для writableTypesForPasteboard:, но я получаю: 'TableViewDataType' не является допустимой строкой UTI. Нельзя использовать недопустимый UTI в качестве типа, возвращаемого из -writeableTypesForPasteboard: в классе TableViewDragNode. - person livings124; 02.01.2012
comment
Если это так, то я не думаю, что у вас есть какой-либо вариант, кроме как объявить собственный UTI для ваших операций перетаскивания в вашем Info.plist. Я согласен, это кажется странным ограничением. Я не верю, что можно определить UTI во время выполнения, он должен быть жестко запрограммирован в Info.plist. - person Rob Keniger; 02.01.2012
comment
В файле rdar://10633989 разрешены пользовательские типы. - person livings124; 02.01.2012
comment
Я не уверен, каковы именно правила, но UTI, возможно, должны соответствовать стандартному формату, чтобы быть действительными, а первые компоненты должны соответствовать домену вашего приложения. Например com.mydomain.myapp.mydatatype. Теперь вы наверняка знаете, что вам нужно! - person Scott Lahteine; 03.04.2012
comment
Ответ Скотта был решением для меня! Я получал ту же ошибку, пока не изменил строку UTI на этот формат. Спасибо, Скотт! - person Elmer Cat; 15.07.2013