7

我在弄清楚如何在 NSTokenField 中表示多对多关系模型时遇到问题。我有两个(相关)模型:

物品标签

一个项目可以有很多标签,一个标签可以有很多项目。所以这是一个反多对关系。

我想做的是在 NSTokenField 中表示这些标签。我想最终得到一个自动建议匹配项的标记字段(找到了一种使用 tokenfield:completionsForSubstring:indexOfToken:indexOfSelectedItem 的方法),并且如果它与现有标记实体不匹配,则能够添加新标记实体。

好吧,希望你还在我身边。我正在尝试使用绑定和数组控制器来完成所有这些工作(因为这是最有意义的,对吧?)

我有一个数组控制器“项目数组控制器”,它绑定到我的应用程序委托 managedObjectContext。显示所有项目的 tableview 绑定到此数组控制器。

我的 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。从带有标签实体的数组转换 -> 带有字符串(标签名称)的数组都很好。另一种方式更具挑战性。

我尝试的是在我的共享应用程序委托托管对象上下文中查找每个名称并返回匹配的标签。这显然给我带来了不同托管对象上下文的问题:

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 很奇怪?)

提前致谢!

4

3 回答 3

7

我已经编写了一个自定义NSValueTransformer来映射绑定NSManagedObject/Tag NSSetNSString NSArray令牌字段的。以下是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 似乎在返回时自动建立对象关系(但我还没有完全验证这一点)

希望能帮助到你。

于 2011-04-19T11:47:06.330 回答
1

Your second error is caused by having two separate managed object context with the same model and store active at the same time. You are trying to create an object in one context and then relate it another object in the second context. That is not allowed. You need to lose the second context and make all your relationships within a single context.

Your initial error is caused by an incomplete keypath. From your description it sounds like you are trying to populate the token fields with ItemsArrayController.selectedItem.tags but that will just return a Tag object which the token filed cannot use. Instead, you need to provide it with something that converts to a string e.g. ItemsArrayController.selectedItem.tags.name

于 2011-04-19T14:12:42.477 回答
0

2个问题:

1)除了您的应用程序委托的上下文之外,您还使用了 NSManagedObjectContext 吗?2) 实现 tokenField:displayStringForRepresentedObject: 的对象是否设置为 NSTokenField 的委托?

于 2011-02-24T13:29:11.930 回答