我正在尝试在NSMutableAttributedString
中存储CoreData
,但由于我的NSMutableAttributedString
的某些属性包含无法存档的Core Foundation对象,因此遇到了问题。有没有一种简单的方法可以让这个对象存储在CoreData中,而不必手动做一些杂乱的东西?
答案 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实体中创建三个属性来实现此拆分:
在自定义实体类中,“内容”将其创建的阴影和属性的更改归结为其阴影。
标题文件:
/**
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问世以来,我现在可以在属性字典中使用NSForegroundColorAttributeName
,NSParagraphStyleAttributeName
,NSFontAttributeName
等,这些键伴随着{{1}等对象可存档的{},UIColor
和NSMutableParagraphStyle
。