Próbuję przechowywać NSMutableAttributedString
w CoreData
, ale mam problemy, ponieważ niektóre z atrybutów moich NSMutableAttributedString
zawierają obiekty Core Foundation, których nie można zarchiwizować. Czy istnieje prosty sposób, aby ten obiekt był przechowywany w CoreData bez konieczności ręcznego robienia bałaganu?Prosty sposób na przechowywanie NSMutableAttributedString w CoreData
Odpowiedz
Zacząłem używać CoreText
, gdy system iOS5 był niedostępny, a tym samym używał wartości Core Foundation jako atrybutów. Jednak teraz zdaję sobie sprawę, że odkąd wyszedł iOS6, mogę teraz używać w słowniku atrybutów: NSForegroundColorAttributeName
, itd. Do klawiszy tych dołączone są takie obiekty, jak UIColor
, NSMutableParagraphStyle
i UIFont
, które można archiwizować.
NSMutableAttributedString
jest zgodny z NSCoding
, co oznacza, że wie, jak przekonwertować się na/z NSData
i robi to za pośrednictwem protokołu, który Core Data wie, jak używać.
Utwórz atrybut "przekształcalny", a następnie po prostu przypisz do niego przypisane łańcuchy. Ponieważ jest to przekształcalne, Core Data użyje NSCoding
, aby przekonwertować go na NSData
po przypisaniu wartości i przekonwertować ją z powrotem na przypisany ciąg, gdy go przeczytasz.
Uwaga: nie można użyć predykatu do filtrowania wyników w tym polu. Ale przechowywanie i pobieranie jest proste.
Jest zgodny z NSCoding, ale możliwe, że jego właściwości nie będą (atrybuty). –
Więc nie jestem pewien, co chce zrobić z przypisywanego ciąg, ale jeśli jest to tekst sformatowany wtedy nie można użyć NSFont, etc ..
Spójrz tutaj http://ossh.com.au/design-and-technology/software-development, napisałem parę rzeczy na temat formatowania stylów i obrazów za pomocą uitextview i nstextview, ale w większości chodzi o przypisane ciągi.
Wszystko to jest przechowywane w danych podstawowych.
Chociaż powyższa odpowiedź jest dobra, to ma jedną wielką wadę:
nie jest możliwe zbudowanie pobrać wniosek/predykat że kwerendy zawartości obiektu NSAttributedString!
Predykat tak spowoduje wyjątek, kiedy wykonywane: jest to potrzebne
[NSPredicate predicateWithFormat:@"(content CONTAINS[cd] %@)", @"test"]];
aby przechowywać „fetchable” NSAttributedString w podstawowych danych do rozlał NSAttributedString na dwie części: po stronie NSString (który może być pobrane) i stronę NSData, która przechowuje atrybuty.
Podział ten może być osiągnięty poprzez utworzenie trzech atrybutów jednostki DANE PODSTAWOWE:
- atrybut cień NSString („contentString”)
- cień NSData atrybutu („contentAttributes”)
- 'undefined' transient attributed ('content')
W niestandardowej klasie encji "treść" przypisana do utworzonej z cienia i zmiany do atrybutu są również lustrem jego cienie.
plik nagłówka: plik
/**
MMTopic
*/
@interface MMTopic : _MMTopic {}
@property (strong, nonatomic) NSAttributedString* content;
@end
Realizacja:
/**
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
Pobieranie mogą być teraz wykonywane przez:
[NSPredicate predicateWithFormat:@"(contentString CONTAINS[cd] %@)", @"test"]];
Chociaż każdy dostęp do NSAttributedString idzie tak:
textField.attributedText = pTopic.content;
Zasady pracy z „atrybutów Non-standard” w danych Core są udokumentowane tutaj: Apple docs
Jak proponujecie przechowywania rzeczy, które nie mogą być archiwizowane? –
@DuncanGroenewald - O to pytam, zgadnij, że to niemożliwe? –
Jakie są konkretne rzeczy, które zawarłeś we właściwościach? Nie możesz ich podklasować i dodać archiwizacji do własnej podklasy. –