2010-04-13 53 views
10

我需要將自定義屬性添加到NSTextView中的選定文本。所以我可以通過獲取選擇的屬性字符串,向它添加一個自定義屬性,然後用新的屬性字符串替換選擇。將自定義屬性保存在NSAttributedString中

所以,現在我將文本視圖的屬性字符串作爲NSData並將其寫入文件。後來,當我打開該文件並將其恢復到文本視圖時,我的自定義屬性消失了!在爲我的自定義屬性制定整個方案之後,我發現自定義屬性不會爲您保存。看看這裏的重要提示:http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/AttributedStrings/Tasks/RTFAndAttrStrings.html

所以我不知道如何保存和恢復我的文件與這個自定義屬性。任何幫助?

回答

15

保存NSAttributedString的正常方式是使用RTF,而RTF數據是NSAttributedString生成的-dataFromRange:documentAttributes:error:方法的內容。

但是,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並將其稱爲新格式。 – 2010-04-13 06:39:29

+0

這是一個很好的答案!感謝Rob和Peter。我可以在沒有輸出爲rtf或rtfd的情況下生活。我想在完成新計劃之後我並沒有想過......我應該自己想到NSCoder。 – regulus6633 2010-04-13 07:44:51

+0

這在iOS中對我來說不太適用。我碰到一個NSCFType encodeWithCoder:,其中的類型對象如下所示: [(kCGColorSpaceDeviceRGB)](0.576471 0.576471 0.560784 1) - 如果您碰巧知道如何我可以編碼這些? – 2013-06-20 20:01:29

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函數在哪裏? – 2011-06-15 09:08:50

+0

我有一個'NSAttributedString',它包含文本和一個或多個'NSTextAttachment'。如果'NSAttributedString'包含'NSTextAttachment',是否可以編輯RTF流(如上所述)並將其轉換回RTF?我在'rtfData = [rtfString dataUsingEncoding:NSASCIIStringEncoding];' – MAH 2016-12-07 00:13:59

相關問題