2010-04-13 23 views
10

È necessario 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.Salvataggio degli attributi personalizzati in NSAttributedString

Così ora ottengo la stringa attribuita della vista 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?

risposta

15

Il modo normale di salvare un NSAttributedString consiste nell'utilizzare RTF e i dati RTF è ciò che genera il metodo -dataFromRange:documentAttributes:error: di NSAttributedString.

Tuttavia, il formato RTF non supporta gli attributi personalizzati. Invece, è necessario utilizzare il protocollo NSCoding per archiviare la stringa attribuito, che manterrà 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]; 
+1

Ovviamente, questi dati non saranno nel formato RTF, quindi non si dovrebbe dare al file l'estensione .rtf. Meglio creare una nuova estensione e UTI e chiamarla un nuovo formato. –

+0

Questa è un'ottima risposta! Grazie Rob e Peter. Posso vivere senza che l'output sia rtf o rtfd. Immagino che non stavo pensando subito dopo aver lavorato al nuovo schema ... Avrei dovuto pensare a NSCoder me stesso. – regulus6633

+0

Questo non funziona in iOS per me. Ottengo un arresto anomalo su un NSCFType encodeWithCoder :, dove l'oggetto tipo è simile a questo: [ (kCGColorSpaceDeviceRGB)] (0.576471 0.576471 0.560784 1) - sarebbe di grande aiuto se sapessi come Posso codificarli? –

5

C'è un modo per salvare attributi personalizzati in formato RTF utilizzando cacao. 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 si potrebbe codificare i 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 compatibili ignoreranno la cui parola chiave non riconoscono "gruppi recitato". 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.

Analogamente, durante la lettura dell'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)]; 
} 
+1

dove si trova la funzione RTFFromRange nel codice sopra? –

+0

Ho un 'NSAttributedString' che contiene testo e uno o più' NSTextAttachment'. È possibile modificare lo stream RTF (come sopra) e riconvertire in RTF se 'NSAttributedString' contiene un' NSTextAttachment'? Continuo a non riuscire in 'rtfData = [rtfString dataUsingEncoding: NSASCIIStringEncoding];' – MAH

Problemi correlati