Salvataggio di attributi personalizzati in NSAttributedString

Devo aggiungere un attributo personalizzato al testo selezionato in un NSTextView. Quindi posso farlo ottenendo la stringa attribuita per la selezione, aggiungendo un attributo personalizzato, e quindi sostituendo la selezione con la mia nuova stringa attribuita.

Così ora ottengo la stringa attribuita dalla vista del testo come NSData e la scrivo in un file. Più tardi, quando apro quel file e lo ripristino nella vista del testo, i miei attributi personalizzati sono spariti! Dopo aver elaborato l’intero schema per il mio attributo personalizzato, trovo che gli attributi personalizzati non vengono salvati per te. Guarda la nota IMPORTANTE qui: http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/AttributedStrings/Tasks/RTFAndAttrStrings.html

Quindi non ho idea di come salvare e ripristinare i miei documenti con questo attributo personalizzato. Qualsiasi aiuto?

Il modo normale di salvare una NSAttributedString consiste nell’utilizzare RTF e i dati RTF sono ciò che viene -dataFromRange:documentAttributes:error: metodo di NSAttributedString .

Tuttavia, il formato RTF non supporta gli attributi personalizzati. Invece, dovresti usare il protocollo NSCoding per archiviare la tua stringa attribuita, che preserverà gli attributi personalizzati:

 //asssume attributedString is your NSAttributedString //encode the string as NSData NSData* stringData = [NSKeyedArchiver archivedDataWithRootObject:attributedString]; [stringData writeToFile:pathToFile atomically:YES]; //read the data back in and decode the string NSData* newStringData = [NSData dataWithContentsOfFile:pathToFile]; NSAttributedString* newString = [NSKeyedUnarchiver unarchiveObjectWithData:newStringData]; 

C’è un modo per salvare attributi personalizzati su RTF usando Cocoa. Si basa sul fatto che RTF è un formato di testo, e quindi può essere manipolato come una stringa anche se non si conoscono tutte le regole di RTF e non si dispone di un lettore / scrittore RTF personalizzato. La procedura che ho delineato di seguito post-processa l’RTF sia in scrittura che in lettura, e ho usato personalmente questa tecnica. Una cosa a cui prestare molta attenzione è che il testo inserito nell’RTF utilizza solo ASCII a 7 bit e nessun carattere di controllo senza caratteri di escape, che includono “\ {}”.

Ecco come codificheresti i tuoi dati:

 NSData *GetRtfFromAttributedString(NSAttributedString *text) { NSData *rtfData = nil; NSMutableString *rtfString = nil; NSString *customData = nil, *encodedData = nil; NSRange range; NSUInteger dataLocation; // Convert the attributed string to RTF if ((rtfData = [text RTFFromRange:NSMakeRange(0, [text length]) documentAttributes:nil]) == nil) return(nil); // Find and encode your custom attributes here. In this example the data is a string and there's at most one of them if ((customData = [text attribute:@"MyCustomData" atIndex:0 effectiveRange:&range]) == nil) return(rtfData); // No custom data, return RTF as is dataLocation = range.location; // Get a string representation of the RTF rtfString = [[NSMutableString alloc] initWithData:rtfData encoding:NSASCIIStringEncoding]; // Find the anchor where we'll put our data, namely just before the first paragraph property reset range = [rtfString rangeOfString:@"\\pard" options:NSLiteralSearch]; if (range.location == NSNotFound) { NSLog(@"Custom data dropped; RTF has no paragraph properties"); [rtfString release]; return(rtfData); } // Insert the starred group containing the custom data and its location encodedData = [NSString stringWithFormat:@"{\\*\\my_custom_keyword %d,%@}\n", dataLocation, customData]; [rtfString insertString:encodedData atIndex:range.location]; // Convert the amended RTF back to a data object rtfData = [rtfString dataUsingEncoding:NSASCIIStringEncoding]; [rtfString release]; return(rtfData); } 

Questa tecnica funziona perché tutti i lettori RTF conformi ignorano i “gruppi speciali” la cui parola chiave non riconosce. Pertanto, vuoi essere sicuro che la tua parola di controllo non verrà riconosciuta da nessun altro lettore, quindi utilizza qualcosa che potrebbe essere univoco, ad esempio un prefisso con la tua azienda o il nome del prodotto. Se i tuoi dati sono complessi, o binari, o potrebbero contenere caratteri RTF illegali che non vuoi sfuggire, codificali in base64. Assicurati di inserire uno spazio dopo la parola chiave.

Allo stesso modo, quando si legge l’RTF, si cerca la parola di controllo, si estraggono i dati e si ripristina l’attributo. Questa routine prende come argomenti la stringa attribuita e l’RTF da cui è stata creata.

 void RestoreCustomAttributes(NSMutableAttributedString *text, NSData *rtfData) { NSString *rtfString = [[NSString alloc] initWithData:rtfData encoding:NSASCIIStringEncoding]; NSArray *components = nil; NSRange range, endRange; // Find the custom data and its end range = [rtfString rangeOfString:@"{\\*\\my_custom_keyword " options:NSLiteralSearch]; if (range.location == NSNotFound) { [rtfString release]; return; } range.location += range.length; endRange = [rtfString rangeOfString:@"}" options:NSLiteralSearch range:NSMakeRange(range.location, [rtfString length] - endRange.location)]; if (endRange.location == NSNotFound) { [rtfString release]; return; } // Get the location and the string data, which are separated by a comma range.length = endRange.location - range.location; components = [[rtfString substringWithRange:range] componentsSeparatedByString:@","]; [rtfString release]; // Assign the custom data back to the attributed string. You should do range checking here (omitted for clarity) [text addAttribute:@"MyCustomData" value:[components objectAtIndex:1] range:NSMakeRange([[components objectAtIndex:0] integerValue], 1)]; }