home link download back number special issue

HMDT - Special Issue / Sketch BP / Sketch に見る書類の読み込み


- Case Study -

Sketch に見る書類の読み込み

NSDocument で書類を読み込む

今度は書類の読み込みの方だ。NSDocument を使って書類を読み込む場合は、次の 3 つのメソッドのどれかを使うんだ。

AppKit/NSDocument.h
- (BOOL)loadDataRepresentation:(NSData *)docData ofType:(NSString *)docType
- (BOOL)loadFileWrapperRepresentation:(NSFileWrapper *)wrapper
                                      ofType:(NSString *)docType
- (BOOL)readFromFile:(NSString *)fileName ofType:(NSString *)docType

引数の type には、書類のタイプが渡されてくるんだ。Sketch では、loadDataRepresentation: を使っているよ。


Property list 表現を NSDictionary にする

NSDictionary に description メソッドを使うと、その property list 表現が得られたよね。じゃ、それを戻すには?NSString の propertyList を使う。

Foundation/NSString.h
- (id)propertyList

返って来る型は、property list によって異なるんだ。NSDictionary の property list なら、NSDictionary が返って来るよ。


Sketch に見る書類の読み込み

Sketch で書類の読み込みをするクラスは、NSDocument を継承した SKTDrawDocument だ。


書類から読み込むもの

書類から読み込むものは、当然ながら、保存したものだ。Sketch で書類に保存したものは次の 3 つだ。

  • ドローオブジェクト
  • バージョン情報
  • プリント情報

これらが NSDictionary の property list の形で保存されているんだ。これを読み込むことになる。


読み込むときの流れ

読み込みに使われるのは、SKTDrawDocument の loadDataRepresentation:ofType: だ。ここから始まって、その流れはこんな感じ。

  • loadDataRepresentation:ofType: は、引数として書類のタイプを取るんで、それをチェックする
  • もし Sketch format だったら、data は NSDictionary の property list 表現なんで、それを NSDictionary に戻す
  • その NSDictionary から SKTGraphic を取り出す
  • それと、バージョン情報も取り出す

で、オッケー。


読み込むフォーマットをチェックする(loadDataRepresentation:ofTyp:

それじゃエントリである loadDataRepresentation:ofType: から見ていこうか。ここでは、まず、書類のタイプをチェックする。SKTDrawDocumentType だったら次に進むぞ。

Sketch/DocumentModesl.subprj/SKTDocument.m
- (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)type {
    if ([type isEqualToString:SKTDrawDocumentType]) {
        NSDictionary *doc = [self drawDocumentDictionaryFromData:data];
        [self setGraphics:[self graphicsFromDrawDocumentDictionary:doc]];

        ...

        return YES;
    } else {
        return NO;
    }
}

SKTDrwaDocumentType だった場合には、data から NSDictionary を作る。それが drawDocumentDictionaryFromData: っていうメソッド。次はそこから SKTGaphic を取り出してやる。それが graphicsFromDrawDocumentDictionary: メソッド。


Property list 表現から NSDictionary を取り出す(drawDocumentDictionaryFromData:

メソッド drawDocumentDictionaryFromData: では、property list 表現を NSDictionary に戻しているんだ。

Sketch/DocumentModesl.subprj/SKTDocument.m
- (NSDictionary *)drawDocumentDictionaryFromData:(NSData *)data {
    NSString *string = [[NSString allocWithZone:[self zone]] 
                    initWithData:data encoding:NSASCIIStringEncoding];
    NSDictionary *doc = [string propertyList];
    
    [string release];

    return doc;
}

まず、data を ASCII の NSString にしてやる。そして、それに対して propertyList メソッドを呼んでやるんだ。これで NSDictionary に戻せる。


SKTGraphic の配列を取り出す(graphicsFromDrawDocumentDictionary:

次にやることは、NSDictionary から SKTGraphic を取り出すことだ。まず、SKTGraphic が入っている NSArray を取り出す必要がある。

Sketch/DocumentModesl.subprj/SKTDocument.m
- (NSArray *)graphicsFromDrawDocumentDictionary:(NSDictionary *)doc {
    NSArray *graphicDicts = [doc objectForKey:SKTGraphicsListKey];
    unsigned i, c = [graphicDicts count];
    NSMutableArray *graphics = [NSMutableArray arrayWithCapacity:c];

    for (i=0; i<c; i++) {
        [graphics addObject:
            [SKTGraphic graphicWithPropertyListRepresentation:
                [graphicDicts objectAtIndex:i]]];
    }

    return graphics;
}

SKTGraphicsListKey を key に使えば、SKTGraphic の NSArray を取り出すことができるんだ。この配列には SKTGraphic を表す NSDictionary が入っている。そいつらを、1 個づつ戻してやればいいんだ。


SKTGraphic を取り出す(graphicWithPropertyListRepresentation:loadPropertyListRepresentation:

戻すには SKTGraphic の graphicWithPropertyListRepresentation: を使う。

その前に、1 つ考えておかなければいけないことがある。ある SKTGraphic を表している NSDictionary があったとしよう。でも、これは実際には、SKTGraphic のサブクラスが実体なんだ。たとえば、SKTRectangle とか、SKTCircle とか。その実体のインスタンスを作って、元に戻してやらないといけない。

どのサブクラスであるかを調べるにはどうするか?SKTClassKey を key として、値を取り出すんだ。

Sketch/DocumentModesl.subprj/SKTGraphic.m
+ (id)graphicWithPropertyListRepresentation:(NSDictionary *)dict {
    Class theClass = NSClassFromString([dict objectForKey:SKTClassKey]);
    id theGraphic = nil;
    
    if (!theClass) {
        theClass = NSClassFromString(
            [@"SKT" stringByAppendingString:
                [dict objectForKey:SKTClassKey]]);
    }
    if (theClass) {
        theGraphic = [[[theClass allocWithZone:NULL] init] autorelease];
        if (theGraphic) {
            [theGraphic loadPropertyListRepresentation:dict];
        }
    }
    return theGraphic;
}

SKTClassKey には、クラスの名前が NSString 型で入っている。それから NSClassFromString() を使えば、Class 型を取り出すことができるんだ。そしたら、そいつのインスタンスを allocWithZone: で作ってやって、loadPropertyListRepresentation: で中身を読み込むんだ。

Sketch/DocumentModesl.subprj/SKTGraphic.m
- (void)loadPropertyListRepresentation:(NSDictionary *)dict {
    id obj;

    obj = [dict objectForKey:SKTBoundsKey];
    if (obj) {
        [self setBounds:NSRectFromString(obj)];
    }
    obj = [dict objectForKey:SKTFillColorKey];
    if (obj) {
        [self setFillColor:[NSUnarchiver unarchiveObjectWithData:obj]];
    }
    obj = [dict objectForKey:SKTDrawsFillKey];
    if (obj) {
        [self setDrawsFill:[obj isEqualToString:@"YES"]];
    }
    obj = [dict objectForKey:SKTStrokeColorKey];
    if (obj) {
        [self setStrokeColor:[NSUnarchiver unarchiveObjectWithData:obj]];
    }
    obj = [dict objectForKey:SKTStrokeLineWidthKey];
    if (obj) {
        [self setStrokeLineWidth:[obj floatValue]];
    }
    obj = [dict objectForKey:SKTDrawsStrokeKey];
    if (obj) {
        [self setDrawsStroke:[obj isEqualToString:@"YES"]];
    }
    return;
}

これは簡単だね。各 key ごとに値を読み込んで、インスタンス変数に代入していけばいいんだ。


違う手段による書類の読み込み

もちろん、違う方法で書類を読み込む手段も、Cocoa は提供している。initWithCoder: を使う方法だ。

Foundation/NSObject.h
@protocol NSCoding

...
- (id)initWithCoder:(NSCoder *)aDecoder;

@end

これを SKTGraphic 実装させる、っていう手もあるよね。


Home | Link | Download | Back Number | Speciall Issue

Sketch BP

mailto: mkino@xd5.so-net.ne.jp

HMDT