2010-04-13 6 views
10

Мне нужно добавить пользовательский атрибут к выбранному тексту в NSTextView. Поэтому я могу сделать это, получив атрибутную строку для выбора, добавив к ней настраиваемый атрибут и затем заменив выделение на мою новую атрибутированную строку.Сохранение пользовательских атрибутов в NSAttributedString

Итак, теперь я получаю строку атрибута текстового представления как NSData и записываю его в файл. Позже, когда я открою этот файл и верну его в текстовое представление, мои пользовательские атрибуты исчезли! После разработки всей схемы для моего пользовательского атрибута я обнаружил, что пользовательские атрибуты не сохраняются для вас. Посмотрите ВАЖНОЕ примечание здесь: http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/AttributedStrings/Tasks/RTFAndAttrStrings.html

Итак, я не знаю, как сохранить и восстановить мои документы с помощью этого настраиваемого атрибута. Любая помощь?

ответ

15

Обычный способ сохранения NSAttributedString - использовать RTF, а данные RTF - это то, что генерирует -dataFromRange:documentAttributes:error: метод NSAttributedString.

Однако формат RTF не поддерживает пользовательские атрибуты. Вместо этого, вы должны использовать протокол NSCoding для архивирования приписываемого строку, которая будет сохранять пользовательские атрибуты:

//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

Конечно, эти данные не будут в формате RTF, поэтому вы не должны указывать в файле расширение .rtf. Лучше всего создать новое расширение и UTI и назвать его новым форматом. –

+0

Это отличный ответ! Спасибо, Роб и Питер. Я могу жить без вывода rtf или rtfd. Наверное, я не думал сразу после разработки новой схемы ... Мне следовало подумать о NSCoder. – regulus6633

+0

Это не работает в iOS для меня. Я получаю сбой на NSCFType encodeWithCoder :, где объект типа выглядит следующим образом: [ (kCGColorSpaceDeviceRGB)] (0.576471 0.576471 0.560784 1) - было бы очень полезно, если бы вы знали, как Я могу кодировать их? –

5

Существует способ сохранить пользовательские атрибуты RTF с использованием какао. Он полагается на то, что RTF является текстовым форматом, поэтому его можно манипулировать как строку, даже если вы не знаете все правила RTF и не имеете пользовательского RTF-ридера/записи. Процедура, описанная ниже, обрабатывает RTF как при записи, так и при чтении, и я использовал эту технику лично. Одно очень важно, так это то, что текст, который вы вставляете в RTF, использует только 7-битный ASCII и не имеет неэкранированных управляющих символов, которые включают «\ {}».

Вот как вы бы кодировать данные:

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); 
} 

Этот метод работает, потому что все совместимые читатели RTF будут игнорировать «избранные группы», чьи ключевые слова они не признают. Поэтому вы хотите быть уверенным, что ваше контрольное слово не будет распознано каким-либо другим читателем, поэтому используйте что-то, что может быть уникальным, например префикс вашей компании или название продукта. Если ваши данные сложны или бинарны или могут содержать незаконные символы RTF, которые вы не хотите убегать, закодируйте его в base64. Убедитесь, что вы поместили пробел после своего ключевого слова.

Аналогично, при чтении RTF вы ищете свое управляющее слово, извлекаете данные и восстанавливаете атрибут. Эта процедура принимает в качестве аргументов атрибутивную строку и RTF, из которых она была создана.

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

Где функция RTFFromRange в приведенном выше коде? –

+0

У меня есть «NSAttributedString», который содержит текст и один или несколько «NSTextAttachment». Можно ли отредактировать RTF-поток (как указано выше) и преобразовать обратно в RTF, если 'NSAttributedString' содержит' NSTextAttachment'? Я продолжаю терпеть неудачу в 'rtfData = [rtfString dataUsingEncoding: NSASCIIStringEncoding];' – MAH

Смежные вопросы