【问题标题】:Saving custom attributes in NSAttributedString在 NSAttributedString 中保存自定义属性
【发布时间】:2010-04-13 02:51:27
【问题描述】:

我需要为 NSTextView 中的选定文本添加自定义属性。因此,我可以通过获取选择的属性字符串,向其中添加自定义属性,然后用我的新属性字符串替换选择来做到这一点。

所以现在我将文本视图的属性字符串作为 NSData 并将其写入文件。稍后当我打开该文件并将其恢复到文本视图时,我的自定义属性消失了!在为我的自定义属性制定整个方案后,我发现自定义属性没有为您保存。看看这里的重要说明:http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/AttributedStrings/Tasks/RTFAndAttrStrings.html

所以我不知道如何使用此自定义属性保存和恢复我的文档。有什么帮助吗?

【问题讨论】:

    标签: objective-c cocoa attributes nsattributedstring


    【解决方案1】:

    保存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];
    

    【讨论】:

    • 当然,这些数据不会是 RTF 格式,所以你不应该给文件 .rtf 扩展名。最好组成一个新的扩展和 UTI,并称之为新格式。
    • 这是一个很好的答案!谢谢罗伯和彼得。我可以在没有输出为 rtf 或 rtfd 的情况下生活。我想我在使用新方案后并没有直接考虑......我自己应该想到 NSCoder。
    • 这对我来说在 iOS 中不太适用。我在 NSCFType encodeWithCoder: 上遇到了崩溃,其中类型对象如下所示: [ (kCGColorSpaceDeviceRGB)] (0.576471 0.576471 0.560784 1) - 如果你碰巧知道如何我可以对这些进行编码吗?
    • 那是因为CGColorRef 不符合NSCoding。看到这个答案:stackoverflow.com/a/10558963/50122
    • CGColorRef 不是一个 Objective-C 对象,它是一个 C 结构体,所以类别的概念并没有真正的意义。
    【解决方案2】:

    有一种方法可以使用 Cocoa 将自定义属性保存到 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)];
    }
    

    【讨论】:

    • 我有一个NSAttributedString,其中包含文本和一个或多个NSTextAttachment。如果NSAttributedString 包含NSTextAttachment,是否可以编辑RTF 流(如上)并转换回RTF?我一直在rtfData = [rtfString dataUsingEncoding:NSASCIIStringEncoding]; 失败
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-12
    • 2017-03-24
    • 1970-01-01
    相关资源
    最近更新 更多