在CoreData中存储NSMutableAttributedString的简单方法

时间:2013-12-18 02:03:56

标签: ios iphone cocoa-touch core-data

我正在尝试在NSMutableAttributedString中存储CoreData,但由于我的NSMutableAttributedString的某些属性包含无法存档的Core Foundation对象,因此遇到了问题。有没有一种简单的方法可以让这个对象存储在CoreData中,而不必手动做一些杂乱的东西?

4 个答案:

答案 0 :(得分:5)

NSMutableAttributedString符合NSCoding,这意味着它知道如何将自身转换为NSData,并通过Core Data知道如何使用的协议进行转换。

使属性"可转换",然后只为其分配属性字符串。由于它是可转换的,因此当您分配值时,Core Data将使用NSCoding将其转换为NSData,并在您阅读时将其转换回属性字符串。

注意,您无法使用谓词来过滤此字段的结果。但存储和检索它很简单。

答案 1 :(得分:5)

虽然上述答案是正确的,但它有一个很大的缺点:

无法构建查询NSAttributedString对象内容的获取请求/谓词!

这样的谓词在执行时会导致异常:

[NSPredicate predicateWithFormat:@"(content CONTAINS[cd] %@)", @"test"]];

要在Core Data中存储'fetchable'NSAttributedString,需要将NSAttributedString分为两部分:NSString端(可以获取)和NSData端,它存储属性。

可以通过在Core Data实体中创建三个属性来实现此拆分:

  1. shadow NSString属性('contentString')
  2. shadow NSData属性('contentAttributes')
  3. 'undefined'瞬态归因('内容')
  4. 在自定义实体类中,“内容”将其创建的阴影和属性的更改归结为其阴影。

    标题文件:

    /**
     MMTopic
    */
    @interface MMTopic : _MMTopic {}
    
    @property (strong, nonatomic) NSAttributedString*   content;
    
    @end
    

    实施档案:

    /**
    MMTopic (PrimitiveAccessors)
    
    */
    
    @interface MMTopic (PrimitiveAccessors)
    
    - (NSAttributedString *)primitiveContent;
    - (void)setPrimitiveContent:(NSAttributedString *)pContent;
    
    @end
    
    
    /**
     MMTopic
    
     */
    @implementation MMTopic    
    
    static NSString const*  kAttributesDictionaryKey =  @"AttributesDictionary";
    static NSString const*  kAttributesRangeKey =       @"AttributesRange";
    
    /*
     awakeFromFetch
    
     */
    - (void)awakeFromFetch {
    
        [super awakeFromFetch];
    
        // Build 'content' from its shadows 'contentString' and 'contentAttributes'
        NSString*                   string = self.contentString;
        NSMutableAttributedString*  content = [[NSMutableAttributedString alloc] initWithString:string];
    
        NSData*                     attributesData = self.contentAttributes;
        NSArray*                    attributesArray = nil;
        if (attributesData) {
            NSKeyedUnarchiver*  decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:attributesData];
            attributesArray = [[NSArray alloc] initWithCoder:decoder];
        }
    
        if ((content) &&
            (attributesArray.count)) {
    
            for (NSDictionary* attributesDictionary in attributesArray) {
                //NSLog(@"%@: %@", NSStringFromRange(((NSValue*)attributesDictionary[kAttributesRangeKey]).rangeValue), attributesDictionary[kAttributesDictionaryKey]);
                [content addAttributes:attributesDictionary[kAttributesDictionaryKey]
                                 range:((NSValue*)attributesDictionary[kAttributesRangeKey]).rangeValue];
            }
    
            [self setPrimitiveContent:content];
        }
    }
    
    /*
     content
    
     */
    @dynamic content;
    
    /*
     content (getter)
    
     */
    - (NSAttributedString *)content {
    
        [self willAccessValueForKey:@"content"];
        NSAttributedString* content = [self primitiveContent];
        [self didAccessValueForKey:@"content"];
    
        return content;
    }
    
    /*
     content (setter)
    
     */
    - (void)setContent:(NSAttributedString *)pContent {
    
        [self willChangeValueForKey:@"content"];
        [self setPrimitiveValue:pContent forKey:@"content"];
        [self didChangeValueForKey:@"content"];
    
        // Update the shadows
        // contentString
        [self setValue:pContent.string
                forKey:@"contentString"];
    
        // contentAttributes
        NSMutableData*      data = [NSMutableData data];
        NSKeyedArchiver*    coder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
        NSMutableArray*     attributesArray = [NSMutableArray array];
        [pContent enumerateAttributesInRange:NSMakeRange(0, pContent.length)
                                     options:0
                                  usingBlock:^(NSDictionary* pAttributesDictionary, NSRange pRange, BOOL* prStop) {
                                      //NSLog(@"%@: %@", NSStringFromRange(pRange), pAttributesDictionary);
                                      [attributesArray addObject:@{
                                                                   kAttributesDictionaryKey:    pAttributesDictionary,
                                                                   kAttributesRangeKey:     [NSValue valueWithRange:pRange],
                                                                   }];
                                  }];
        [attributesArray encodeWithCoder:coder];
        [coder finishEncoding];
    
        [self setValue:data
                forKey:@"contentAttributes"];
    }
    
    @end
    

    现在可以通过以下方式获取:

    [NSPredicate predicateWithFormat:@"(contentString CONTAINS[cd] %@)", @"test"]];
    

    虽然对NSAttributedString的任何访问都是这样的:

    textField.attributedText = pTopic.content;
    

    此处记录了核心数据中使用“非标准属性”的规则:Apple docs

答案 2 :(得分:1)

嗯,我不确定您要对属性字符串做什么,但如果是格式化文本,则不能使用NSFont等。

看看这里http://ossh.com.au/design-and-technology/software-development,我用uitextview和nstextview在格式化样式和图像上发布了一些内容,但主要是关于归因字符串。

这些东西都存储在核心数据中。

答案 3 :(得分:1)

当iOS5用完时我开始使用CoreText,因此使用Core Foundation值作为属性。但是我现在意识到,自从iOS6问世以来,我现在可以在属性字典中使用NSForegroundColorAttributeNameNSParagraphStyleAttributeNameNSFontAttributeName等,这些键伴随着{{1}等对象可存档的{},UIColorNSMutableParagraphStyle