2010-10-07 6 views
7

У меня возникли проблемы с выяснением того, как представить модель отношения «многие ко многим» в NSTokenField. У меня есть два (соответствующая) модель:NSTokenField, представляющий отношение Core Data to-many

Элемент Tag

Элемент может иметь много тегов и тег может иметь много вещей. Так что это отношение обратное к многим.

Что я хотел бы сделать, это представить эти теги в NSTokenField. Я бы хотел, чтобы в конечном итоге поле tokenfield автоматически предложило совпадения (выяснил способ сделать это с помощью tokenfield: completionsForSubstring: indexOfToken: indexOfSelectedItem) и возможность добавлять новые теги, если он не был сопоставлен с существующим.

Хорошо, надеюсь, что вы все еще со мной. Я пытаюсь сделать все это с привязками и контроллерами массивов (так как это имеет смысл, верно?)

У меня есть контроллер массива «Контроллер массива элементов», который привязан к моим делегатам приложений managedObjectContext. Таблица, показывающая все элементы, имеет привязку к этому контроллеру массива.

Значение моего NSTokenField имеет привязку к ключу выбора контроллеров массива и к тегу.

С помощью этой конфигурации NSTokenField не будет показывать теги. Это просто дает мне:

<NSTokenFieldCell: 0x10014dc60>: Unknown object type assigned (Relationship objects for {(
    <NSManagedObject: 0x10059bdc0> (entity: Tag; id: 0x10016d6e0 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Tag/p102> ; data: <fault>) 
)} on 0x100169660). Ignoring... 

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

- (NSString *)tokenField:(NSTokenField *)tokenField displayStringForRepresentedObject:(id)representedObject 

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

Хорошо, поэтому моим следующим шагом было попытаться создать ValueTransformer. Преобразование из массива с тегом entity -> array со строками (имена тегов) было хорошим. Другой путь более сложный.

То, что я пробовал, состоит в том, чтобы искать каждое имя в контексте объекта управляемого объекта делегирования приложения и возвращать соответствующие теги. Это, по-видимому, вызывает проблему с различными контекстами управляемых объектов:

Illegal attempt to establish a relationship 'tags' between objects in different contexts (source = <NSManagedObject: 0x100156900> (entity: Item; id: 0x1003b22b0 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Item/p106> ; data: { 
author = "0x1003b1b30 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Author/p103>"; 
createdAt = nil; 
filePath = nil; 
tags =  (
); 
title = "Great presentation"; 
type = "0x1003b1150 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Type/p104>"; 
}) , destination = <NSManagedObject: 0x114d08100> (entity: Tag; id: 0x100146b40 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Tag/p102> ; data: <fault>)) 

Где я иду не так? Как это разрешить? Это даже правильный подход (кажется мне странным, что вам нужно использовать ValueTransformer?)

Заранее спасибо!

+0

Я потратил еще некоторое время на изучение этого сегодня - до сих пор не могу найти ресурсы, объясняющие это. Надеюсь, кто-то придет на помощь здесь! :) – simonwh

ответ

7

Я написал пользовательский NSValueTransformer для отображения между связанной NSManagedObject/TagNSSet и NSStringNSArray токена поля.Вот 2 способа:

- (id)transformedValue:(id)value { 
    if ([value isKindOfClass:[NSSet class]]) { 
    NSSet *set = (NSSet *)value; 
    NSMutableArray *ary = [NSMutableArray arrayWithCapacity:[set count]]; 
    for (Tag *tag in [set allObjects]) { 
     [ary addObject:tag.name]; 
    } 
    return ary; 
    } 
    return nil; 
} 

- (id)reverseTransformedValue:(id)value { 
    if ([value isKindOfClass:[NSArray class]]) { 
    NSArray *ary = (NSArray *)value; 
    // Check each NSString in the array representing a Tag name if a corresponding 
    // tag managed object already exists 
    NSMutableSet *tagSet = [NSMutableSet setWithCapacity:[ary count]]; 
    for (NSString *tagName in ary) { 
     NSManagedObjectContext *context = [[NSApp delegate] managedObjectContext]; 
     NSFetchRequest *request = [[NSFetchRequest alloc] init]; 

     NSPredicate *searchFilter = [NSPredicate predicateWithFormat:@"name = %@", tagName]; 
     NSEntityDescription *entity = [NSEntityDescription entityForName:[Tag className] inManagedObjectContext:context]; 

     [request setEntity:entity]; 
     [request setPredicate:searchFilter]; 

     NSError *error = nil; 
     NSArray *results = [context executeFetchRequest:request error:&error]; 
     if ([results count] > 0) { 
     [tagSet addObjectsFromArray:results]; 
     } 
     else { 
     Tag *tag = [[Tag alloc] initWithEntity:entity insertIntoManagedObjectContext:context]; 
     tag.name = tagName; 

     [tagSet addObject:tag]; 
     [tag release]; 
     } 
    } 
    return tagSet; 
    } 
    return nil; 
} 

CoreData, кажется, автоматически устанавливает объектные отношения по возвращению (но я еще не полностью проверил это пока)

Надеется, что это помогает.

0

2 вопросы:

1) Есть ли у вас NSManagedObjectContext используется, кроме контекста своего приложения делегата? 2) Объект, который реализует tokenField: displayStringForRepresentedObject: устанавливается как делегат для NSTokenField?

1

Ваша вторая ошибка вызвана наличием двух отдельных контекстов управляемого объекта с одной и той же моделью и одновременным сохранением. Вы пытаетесь создать объект в одном контексте, а затем связать его с другим объектом во втором контексте. Это недопустимо. Вам нужно потерять второй контекст и сделать все ваши отношения в рамках одного контекста.

Ваша первоначальная ошибка вызвана незавершенностью ключа. Из вашего описания это похоже на то, что вы пытаетесь заполнить поля токена ItemsArrayController.selectedItem.tags, но это просто вернет объект Tag, который не может использовать указанный токен. Вместо этого вы должны предоставить ему что-то, что преобразуется в строку, например. ItemsArrayController.selectedItem.tags.name