0

根据这个问题的公认答案,我编写了以下代码:

NSData* somedata;
somedata=[NSKeyedArchiver archivedDataWithRootObject:ts];

其中 ts 是一个 NSAttributedString ,其中填充了一些文本和一些属性(在本例中为颜色)。

当我执行此代码时,我收到此错误:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFType encodeWithCoder:]: unrecognized selector sent to instance 0x6eb5b90'

我是 NSCoder 领域的新手,但上述问题的答案让我觉得这就是我所要做的。是吗?我错过了什么?


编辑:

在这种情况下,无法识别的选择器被发送到 NSAttributedString 中的颜色属性。当我像这样初始化字符串时:

NSAttributedString *ts = [[NSAttributedString alloc] initWithString:text attributes:self.currentAttributeDictionary];

字典是这样构建的:

self.currentAttributeDictionary=[NSDictionary dictionaryWithObjectsAndKeys:
                                 [self.currentColor CGColor],(NSString*)kCTForegroundColorAttributeName,
                                 nil];

字典的 NSLog 产生了这个:

New dictionary is: {
CTForegroundColor = "<CGColor 0x6eb5b90> [<CGColorSpace 0x6e968c0> (kCGColorSpaceDeviceRGB)] ( 1 1 0 1 )";}

上面的 CGColor 的地址与错误消息中的地址匹配。

4

2 回答 2

6

虽然UIColor符合NSCoding,但(与大多数此类课程不同)不是免费桥接到CGColorRef. 您的字典正在尝试对其内容进行编码,CGColorRef但不知道如何对其进行编码。

假设您不想编码 a UIColor(因为这些听起来像 Core Text 属性),您将不得不自己处理序列化CGColorRef。例如,请参阅这个问题以获得一些有用的想法。

应该注意的是,顺便说一句,由于我不知道存档数据的去向,如果您想在 OS X 上取消存档数据,那么颜色在 AppKit/UIKit 级别再次成为令人头疼的问题:NSColor并且UIColor不直接兼容,所以你仍然需要通过CGColorRef,适当地存储色彩空间信息。

于 2012-05-11T21:35:53.073 回答
0

根据要求,这是我用来完成我需要完成的代码的代码。自从我查看这段代码以来已经有一年了,编写它更多的是为了了解正在发生的事情,而不是为了伟大的编码实践或任何形式的效率。然而,它确实奏效了,而且效果很好!

我定义了一个NSAttributedString类的代码如下。

示例使用:

-(void)code:(id)sender {    
    self.testData=[textView.attributedString customEncode];
    NSLog(@"%@",self.testData);
}

-(void)recover:(id)sender {
    NSAttributedString* tString=[NSMutableAttributedString customDecode:self.testData];
    NSLog(@"Recover pressed: %@",tString);
    textView.attributedString=tString;
}

这是底层代码:

#import "NSAttributedString+Extras.h"
#import <CoreText/CoreText.h>

@implementation NSAttributedString (Extras)

-(NSData*)customEncode {
    __block NSMutableArray* archivableAttributes=[[NSMutableArray alloc]init];

    [self enumerateAttributesInRange:NSMakeRange(0, [self length]) options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
        NSLog(@"range: %d %d",range.location, range.length);
        NSLog(@"dict: %@",attrs);
        NSLog(@"keys: %@", [attrs allKeys]);
        NSLog(@"values: %@", [attrs allValues]);

        NSMutableDictionary* tDict=[[NSMutableDictionary alloc]init];

        [tDict setObject:[NSNumber numberWithInt:range.location] forKey:@"location"];
        [tDict setObject:[NSNumber numberWithInt:range.length] forKey:@"length"];

        for (NSString* tKey in [attrs allKeys]) {
            if ([tKey isEqualToString:@"CTUnderlineColor"]) {
                [tDict setObject:[NSAttributedString arrayFromCGColorComponents:((CGColorRef)[attrs objectForKey:@"CTUnderlineColor"])] forKey:@"CTUnderlineColor"];
            }
            if ([tKey isEqualToString:@"NSUnderline"]) {
                NSNumber* underline=[attrs objectForKey:@"NSUnderline"];
                [tDict setObject:underline forKey:@"NSUnderline"];
            }
            if ([tKey isEqualToString:@"CTForegroundColor"]) {
                [tDict setObject:[NSAttributedString arrayFromCGColorComponents:((CGColorRef)[attrs objectForKey:@"CTForegroundColor"])] forKey:@"CTForegroundColor"];
            }
            if ([tKey isEqualToString:@"NSFont"]) {
                CTFontRef font=((CTFontRef)[attrs objectForKey:@"NSFont"]);

                NSDictionary* fontDict=[NSDictionary 
                                        dictionaryWithObjects:
                                        [NSArray arrayWithObjects:(NSString*)CTFontCopyPostScriptName(font),[NSNumber numberWithFloat:CTFontGetSize(font)], nil]
                                        forKeys:
                                        [NSArray arrayWithObjects:@"fontName", @"fontSize", nil]];

                [tDict setObject:fontDict forKey:@"NSFont"];
            }
        }

        [archivableAttributes addObject:tDict];
    }];

    NSMutableDictionary* archiveNSMString=[NSMutableDictionary 
                                           dictionaryWithObjects: [NSArray arrayWithObjects:[self string],archivableAttributes,nil]
                                           forKeys:[NSArray arrayWithObjects:@"string",@"attributes",nil]];

    NSLog(@"archivableAttributes array: %@",archiveNSMString);

    NSData* tData=[NSKeyedArchiver archivedDataWithRootObject:archiveNSMString];

    NSLog(@"tdata: %@",tData);

    return tData;
}

+(NSAttributedString*)customDecode:(NSData *)data {
    NSMutableAttributedString* tString;
    NSMutableDictionary* tDict=[NSKeyedUnarchiver unarchiveObjectWithData:data];
    NSArray* attrs;

    CTFontRef font=NULL;
    CGColorRef color=NULL;
    NSNumber* underlineProp=[NSNumber numberWithInt:0];
    CGColorRef underlineColor=NULL;

    NSLog(@"decoded dictionary: %@",tDict);

    if ([[tDict allKeys]containsObject:@"string"]) {
        tString=[[NSMutableAttributedString alloc]initWithString:((NSString*)[tDict objectForKey:@"string"])];
    }
    else {
        tString=[[NSMutableAttributedString alloc]initWithString:@""];
    }

    if ([[tDict allKeys]containsObject:@"attributes"]) {
        attrs=[tDict objectForKey:@"attributes"];
    }
    else {
        attrs=nil;
    }

    for (NSDictionary* attDict in attrs) {
        int location=-1;
        int length=-1;
        NSRange insertRange=NSMakeRange(-1, 0);

        if ([[attDict allKeys]containsObject:@"location"]) {
            location=[[attDict objectForKey:@"location"]intValue];
        }
        if ([[attDict allKeys]containsObject:@"length"]) {
            length=[[attDict objectForKey:@"length"]intValue];
        }
        if (location!=-1&&length!=-1) {
            insertRange=NSMakeRange(location, length);
        }

        if ([[attDict allKeys]containsObject:@"NSUnderline"]) {
            underlineProp=[attDict objectForKey:@"NSUnderline"];
        }

        if ([[attDict allKeys]containsObject:@"CTUnderlineColor"]) {
            underlineColor=[NSAttributedString cgColorRefFromArray:[attDict objectForKey:@"CTUnderlineColor"]];
        }        

        if ([[attDict allKeys]containsObject:@"CTForegroundColor"]) {
            color=[NSAttributedString cgColorRefFromArray:[attDict objectForKey:@"CTForegroundColor"]];
        }

        if ([[attDict allKeys]containsObject:@"NSFont"]) {
            NSString* name=nil;
            float size=-1;

            NSDictionary* fontDict=[attDict objectForKey:@"NSFont"];

            if ([[fontDict allKeys]containsObject:@"fontName"]) {
                name=[fontDict objectForKey:@"fontName"];
            }
            if ([[fontDict allKeys]containsObject:@"fontSize"]) {
                size=[[fontDict objectForKey:@"fontSize"]floatValue];
            }

            if (name!=nil&&size!=-1) {
                font=CTFontCreateWithName((CFStringRef)name, size, NULL);
            }
        }

        if (insertRange.location!=-1) {
            if (color!=NULL) {
                [tString addAttribute:(NSString*)kCTForegroundColorAttributeName value:(id)color range:insertRange];
            }
            if (font!=NULL) {
                [tString addAttribute:(NSString*)kCTFontAttributeName value:(id)font range:insertRange];
            }
            if ([underlineProp intValue]!=0&&underlineColor!=NULL) {
                [tString addAttribute:(NSString*)kCTUnderlineColorAttributeName value:(id)underlineColor range:insertRange];
                [tString addAttribute:(NSString*)kCTUnderlineStyleAttributeName value:(id)underlineProp range:insertRange];
            }
       }
    } 

    [tString enumerateAttributesInRange:NSMakeRange(0, [tString length]) options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
        NSLog(@"range: %d %d",range.location, range.length);
        NSLog(@"dict: %@",attrs);
        NSLog(@"keys: %@", [attrs allKeys]);
        NSLog(@"values: %@", [attrs allValues]);
    }];

    return [[NSAttributedString alloc]initWithAttributedString:tString];
}

+(NSArray*)arrayFromCGColorComponents:(CGColorRef)color {
    int numComponents=CGColorGetNumberOfComponents(color);
    CGFloat* components=CGColorGetComponents(color);
    NSMutableArray* retval=[[NSMutableArray alloc]init];
    for(int i=0;i<numComponents;i++) {
        [retval addObject:[NSNumber numberWithFloat:components[i]]];
    }
    return [NSArray arrayWithArray:retval];
}

+(CGColorRef)cgColorRefFromArray:(NSArray*)theArray {
    CGFloat* array=malloc(sizeof(CGFloat)*[theArray count]);
    for (int i=0; i<[theArray count]; i++) {
        array[i]=[[theArray objectAtIndex:i]floatValue];
    }

    CGColorSpaceRef theSpace;

    if ([theArray count]==2) {
        theSpace=CGColorSpaceCreateDeviceGray();
    }
    else {
        theSpace=CGColorSpaceCreateDeviceRGB();
    }

    return CGColorCreate(theSpace, array);
}

@end
于 2013-07-03T20:48:18.647 回答