2010-10-07 7 views
7

Ich habe ein Problem herauszufinden, wie man ein Viele-zu-Viele-Beziehungsmodell in einem NSTokenField darstellt. Ich habe zwei (relevanten) Modelle:NSTokenField, das die Core-to-Many-Beziehung darstellt

Artikel Tag

Ein Element kann viele Tags haben und ein Tag viele Elemente haben kann. Es ist also eine Inverse-zu-Viele-Beziehung.

Ich möchte diese Tags in einem NSTokenField darstellen. Ich würde gerne mit einem Token-Feld enden, das automatisch Übereinstimmungen vorschlägt (fand einen Weg, dies mit tokenfield zu tun: completionsForSubstring: indexOfToken: indexOfSelectedItem) und in der Lage neue Tag-Entities hinzuzufügen, wenn es nicht mit einem existierenden übereinstimmte.

Okay, hoffe, du bist immer noch bei mir. Ich versuche all dies mit Bindings und Array-Controllern (da macht das am meisten Sinn, oder?)

Ich habe einen Array-Controller, "Element Array Controller", der an meine App Delegaten ManagedObjectContext gebunden ist. Eine Tabellenansicht mit allen Elementen ist an diesen Array-Controller gebunden.

Der Wert meines NSTokenFields ist an den Array-Controller-Auswahlschlüssel und den Modellschlüsselpfad: Tags gebunden.

Mit dieser Konfiguration zeigt das NSTokenField die Tags nicht an. Es gibt mir nur:

<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... 

Dies macht Sinn für mich, also keine Sorgen. Ich habe einige der NSTokenField Delegatmethoden sah und es scheint, dass ich verwenden sollte:

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

Problem ist, ist diese Methode nicht aufgerufen, und ich bekomme den gleichen Fehler wie zuvor.

In Ordnung, also war mein nächster Schritt, einen ValueTransformer zu bauen. Die Transformation von einem Array mit Tag-Entity -> Array mit Strings (Tag-Namen) war alles in Ordnung. Der andere Weg ist schwieriger.

Was ich versucht habe, ist, jeden Namen in meinem freigegebenen App delegierten Objektkontext nachschlagen und die passenden Tags zurückgeben. Das gibt mir ein Problem mit anderen verwalteten Objekt Kontexten offenbar:

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>)) 

Wohin gehe ich falsch? Wie kann ich das beheben? Ist es sogar der richtige Ansatz (scheint mir seltsam, dass Sie einen ValueTransformer verwenden müssten?)

Vielen Dank im Voraus!

+0

Ich habe heute etwas mehr Zeit damit verbracht, dies zu erforschen - immer noch nicht in der Lage, Ressourcen zu finden, die das erklären. Ich hoffe, dass hier jemand zur Rettung kommt! :) – simonwh

Antwort

7

Ich habe eine benutzerdefinierte geschrieben NSValueTransformer zwischen dem gebundenen NSManagedObject/TagNSSet und der NSStringNSArray des Token-Feld abzubilden.Hier sind die zwei Methoden:

- (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 die Objektbeziehungen auf Rückkehr automatisch zu etablieren scheint (aber ich habe nicht vollständig diese noch überprüft)

Hoffe, es hilft.

0

2 Fragen:

1) Haben Sie eine NSManagedObjectContext andere verwendet werden als Ihr Kontext des App delegieren? 2) Ist das Objekt, das TokenField implementiert: displayStringForRepreneObject: als Delegat für das NSTokenField gesetzt?

1

Ihr zweiter Fehler wird dadurch verursacht, dass zwei separate verwaltete Objektkontexte mit demselben Modell und demselben Speicher gleichzeitig aktiv sind. Sie versuchen, ein Objekt in einem Kontext zu erstellen und dann ein anderes Objekt im zweiten Kontext zu verknüpfen. Das ist nicht erlaubt. Sie müssen den zweiten Kontext verlieren und alle Ihre Beziehungen in einem einzigen Kontext abbilden.

Ihr anfänglicher Fehler wird durch einen unvollständigen Schlüsselpfad verursacht. Aus Ihrer Beschreibung klingt es, als ob Sie versuchen, die Token-Felder mit ItemsArrayController.selectedItem.tags zu füllen, aber das wird nur ein Tag Objekt zurückgeben, das das Token-Feld nicht verwenden kann. Stattdessen müssen Sie es mit etwas versehen, das in eine Zeichenfolge konvertiert, z.