【问题标题】:Error unarchiving NSAttributedString on iOS13 with NSKeyedUnarchiver when NSTextAttachment is used使用 NSTextAttachment 时使用 NSKeyedUnarchiver 在 iOS13 上取消归档 NSAttributedString 时出错
【发布时间】:2021-06-01 20:05:16
【问题描述】:

我在 iOS14 上存档了一个 NSAttributedString,其中包含带有 NSTextAttachment 的图像,并注意到在 iOS13 上取消存档失败。在 iOS14 上解压成功。

记录的错误是这样的:

Error decoding string object. error=Error Domain=NSCocoaErrorDomain Code=4864 
"value for key 'NS.objects' was of unexpected class 'NSTextAttachment'. Allowed classes are '{(
    NSGlyphInfo,
    UIColor,
    NSURL,
    UIFont,
    NSParagraphStyle,
    NSString,
    NSAttributedString,
    NSArray,
    NSNumber,
    NSDictionary
)}'." UserInfo={NSDebugDescription=value for key 'NS.objects' was of unexpected class 'NSTextAttachment'. Allowed classes are '{(
    NSGlyphInfo,
    UIColor,
    NSURL,
    UIFont,
    NSParagraphStyle,
    NSString,
    NSAttributedString,
    NSArray,
    NSNumber,
    NSDictionary
)}'.}

有没有办法让它发挥作用,还是我注定要在这里?

这是使用NSTextAttachment 将图像插入NSMutableAttributedString 的方式:

// Add an image:
NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
textAttachment.image = image;
NSMutableAttributedString *strWithImage = [[NSAttributedString attributedStringWithAttachment:textAttachment] mutableCopy];

[s appendAttributedString:strWithImage];

这是存档行:

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:str requiringSecureCoding:YES error:&error];

这是取消归档的行,它返回一个NSError 实例:

NSError *error = nil;
NSAttributedString *str = [NSKeyedUnarchiver unarchivedObjectOfClass:NSAttributedString.class fromData:data error:&error];

我在 iOS14 上创建了 NSData 实例,将其存储到文件中并在 iOS13 中读取。这是失败的时候。

【问题讨论】:

    标签: ios ios13 nsattributedstring ios14 nssecurecoding


    【解决方案1】:

    错误清楚地表明NSTextAttachment 无法解码,因为它不在受支持的类列表中。

    您必须找到一种解决方法才能使其在 iOS 13 或更低版本上运行。

    可能的解决方案

    1. NSMutableAttributedString 实例中删除所有附件。
    2. archive 和你今天一样。
    3. [TextAttachmentInfo] JSON 数据与上述数据配对编码并存储。
    4. unarchive 和你今天一样。
    5. 将配对的 JSON 数据解码为[TextAttachmentInfo]
    6. NSTextAttachments 插入到未归档的NSMutableAttributedString 实例中

    这里有一些帮助代码,需要根据您自己的用例进一步调整和测试。

    extension NSMutableAttributedString {
        
        struct TextAttachmentInfo: Codable {
            let location: Int
            let imageData: Data
        }
        
        func removeAllTextAttachments() -> [TextAttachmentInfo] {
            guard self.length > 0 else { return [] }
            
            var textAttachmentInfos: [TextAttachmentInfo] = []
            
            for location in (0..<self.length).reversed() {
                var effectiveRange = NSRange()
                let attributes = self.attributes(at: location, effectiveRange: &effectiveRange)
                
                for (_, value) in attributes {
                    if let textAttachment = value as? NSTextAttachment,
                       let data = textAttachment.image?.pngData() {
                        self.replaceCharacters(in: effectiveRange, with: "")
                        textAttachmentInfos.append(.init(location: effectiveRange.location, imageData: data))
                    }
                }
            }
            
            return textAttachmentInfos.reversed()
        }
        
        func insertTextAttachmentInfos(_ infos: [TextAttachmentInfo]) {
            for info in infos {
                let attachment = NSTextAttachment()
                attachment.image = UIImage(data: info.imageData)
                self.insert(NSAttributedString(attachment: attachment), at: info.location)
            }
        }
        
    }
    

    【讨论】:

    • 我已经从错误描述中看到了。您有解决方法的提示吗?
    • @RainerSchwarze 我添加了一个可能的解决方案。
    【解决方案2】:

    我的一个解决方案是在包含NSAttributedString 的类的initWithCoder: 方法中提供可接受类的列表。 (感谢 Markus Spoettl 几年前在 cocoa-dev 邮件列表中提供的解决方案。)

    -(instancetype)initWithCoder:(NSCoder *)coder {
        self = [super init];
        if (self) {
            NSAttributedString *str = nil;
            // This fails on iOS13:
            // str = [coder decodeObjectOfClass:NSAttributedString.class forKey:@"attrStr"];
    
            // Provide a set of classes to make NSTextAttachment accepted:
            str = [coder decodeObjectOfClasses:[NSSet setWithObjects:
                NSAttributedString.class, 
                NSTextAttachment.class, nil] forKey:@"attrStr"];
            self.attrStr = str;
        }
        return self;
    }
    

    这似乎是与NSAttributedString 一起使用的其他类的问题,例如NSTextTab 等。因此,根据用例,可以扩展可接受的类列表。

    【讨论】:

      猜你喜欢
      • 2018-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-01
      • 1970-01-01
      相关资源
      最近更新 更多