[iOS] iOS5以下 + Xcode4.4でもObjective-Cの新シンタックスで配列、辞書の値を取得したい

2012/07/27

こんにちは。きんくまです。またタイトル長いです。

ちょっと前に、Objective-Cでも新シンタックスで楽にかけるようになるらしいという記事を書きました。
>> [iOS] 秋ぐらいに出る?らしいXcode4.5でコードを少し短くかけるみたい

そしたら、機能のMountain Lionさんと一緒にリリースされたXcode4.4でもできるらしいじゃないですか。
>> 今日からライオンでも使える!Xcode4.4 Modern Objective-C Syntaxでコードをきれいにする方法 | Zero4Racer PRO Developer’s Blog

ほうほうこれは便利そうだということで、早速ダウンロードして試してみたところ、

・NSNumberの@を使った書き方
・空カテゴリでメソッドを定義しなくてもOK
・@synthesizeをかかなくてもよい

なところはできました。

配列と辞書の値の取得ができない

それで配列と、辞書のところで詰まりました。
値の設定はできるのですが、値の取得ができないのです。

    NSDictionary *dic = @{
         @"name":@"Mario"
        ,@"age":@"33"
        ,@"method":^{
            NSLog(@"Hello world");
        }
    };
    NSLog(@"name: %@, age: %@", dic[@"name"], dic[@"age"]); //ここでエラー
Expected method to read dictionary element not found on object of type *NSDictionary

上記のエラーが出ます。困ったなあと思い、ググってみると解決方法が見つかりました。
良かった良かった。

>> How to enable the new Objective-C object literals on iOS?
>> Using Subscripting With Xcode 4.4 and iOS 4.3+ (Peter Steinberger)

注意点

まず注意点をまとめると、

・iOS6からはこれからする解決方法をしなくてもよい。iOS5以下の場合に行う。
・LLVM4.0コンパイラーが必要(これがXcode4.4に入ってる)
・短縮系の書き方をするとARCLiteが書き換えてくれる
・短縮系はコンパイル時に変換されるもので、実行時(ランタイム)にされるものではない

ARCLiteというのはARC使ったときに使われるファイルらしいです。
ARCLite.mというのがあるみたいですが詳しいことはわかりませんです。
なので、ARCを使ってない人は
Linking > Other Linker Flags に -fobjc-arc をつけてね。

って書いてあったけど、私のはつけてなくても動いてます。よくわかんない。

解決方法

NSObject+subscripts.hというカテゴリを用意してこんなコードを書きます。
実装(.m)ファイルは必要ないです。ARCLiteが勝手にやってくれるらしい。

#import <Foundation/Foundation.h>

#if __IPHONE_OS_VERSION_MAX_ALLOWED < 60000
@interface NSObject (PSPDFSubscriptingSupport)

- (id)objectAtIndexedSubscript:(NSUInteger)idx;
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx;
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;
- (id)objectForKeyedSubscript:(id)key;

@end
#endif

__IPHONE_OS_VERSION_MAX_ALLOWED < 60000 という部分がiOSのバージョンチェックですね。 iOS5以下の場合のみのターゲットにしてます。 上のはNSObjectに全て書いてしまってますが、きちんと書きたいんだ!という場合はこんなふうに書くみたいです

#if __IPHONE_OS_VERSION_MAX_ALLOWED < 60000
@interface NSDictionary(subscripts)
– (id)objectForKeyedSubscript:(id)key;
@end

@interface NSMutableDictionary(subscripts)
– (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;
@end

@interface NSArray(subscripts)
– (id)objectAtIndexedSubscript:(NSUInteger)idx;
@end

@interface NSMutableArray(subscripts)
– (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx;
@end
#endif

このカテゴリヘッダーを短縮系を使いたいところにimportすればOKです。
もしくは、全体に適用させたい場合は .pchファイルにこんな感じに追加しておきます。

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    #import "NSObject+subscripts.h" //追加
#endif

実際に使ってみる

blocksを使うときは、一度変数に代入したらうまくいった。

    NSDictionary *dic = @{
         @"name":@"Mario"
        ,@"age":@"33"
        ,@"method":^{
            NSLog(@"Hello world");
        }
    };
    NSLog(@"name: %@, age: %@", dic[@"name"], dic[@"age"]);
    void (^hello)(void) = dic[@"method"];
    hello();
    
    
    NSArray *arr = @[
         @"Peach"
        , @"27"
        , ^(int a, int b){
            return a + b;
    }];
    
    NSLog(@"name: %@, age: %@", arr[0], arr[1]);
    int (^add)(int,int) = arr[2];
    NSLog(@"5 + 3 = %d",add(5,3));

念のためもう一度書いておくと、今回の処理はiOS6以上では必要ないということです。
移行のための仮処置という。

あと、もっと詳しい機能について、例えば@synthesize時のインスタンス変数のこととかは
下記のページで勉強できます。

@shu223さんのツイートから

LINEで送る
Pocket

[iOS] NSXMLParser。SAXとDOMの比較とか

2012/07/22

こんにちは。きんくまです。

前回書いたNSXMLParserについてTNKさんからコメント欄で情報をいただきまして、調べてみました。

NSXMLParserはSAXパーサーで、DOMパーサーとは違うとのことです。
そういえば、使い方を調べたときにそんなことを書いてあったような。あんまり深く考えずに使ってました。

Wikiによると、SAX(Simple API for XML)はDOMに比べてこんな特徴があります。英語Wikiも参照

SAXには公式の仕様というものはないみたいです。

SAX DOM
DOMツリーを作らない DOMツリーを作る
イベントをベースにパース イベントベースでない
最小のメモリで済む
(DOMツリー全体を保持せず1部分だけを保持するから)
メモリが多く必要
(DOMツリー全体を保持するから)
速い 遅い
パース中に状態管理(特定のタグの中にいるとかいないとか)が必要 パース中は何もしないので状態管理はいらない
XMLが正しい形かどうかチェックできない(データを全て保持しないため) XMLが正しい形かチェックできる

表だけみるとSAXの方がいいことが多そうに見えますが、
イメージ的にはDOMを構築してる途中データがSAXでは取得される感じですね。

DOMはパースが終わればツリーが完成しているので、そのあとにDOMツリーをたどって中身にアクセスしてきます。
それに対してSAXはツリーを作らず、タグの開始 or 終了、文字をみつけたといったイベントのときに、その中身をプログラマが処理していく手動な感じです。

NSXMLParserがSAXタイプなのは、携帯端末がPCに比べて少ないメモリであるためでしょうね。

NSXMLParser

昨日の文字列が途中できれてイベントが発行される(1つのタグ内で複数回文字発見イベント発行)のは、もとからのようです。
さらに日本語とか英語とか関係なく、起こるようです。コメント欄でいただいたリンクです。

>> iPhone: NSXMLParser’s foundCharacters method called multiple time for single tag

なので、NSXMLParserでは
1. タグ開始イベントで文字列初期化
2. 文字見つけたよイベントで、保持してた文字と連結
3. 2が複数回に分けて起こる場合があるので繰り返して連結
4. タグ終了イベントで最終的に連結された文字列で何かする

という手順がデフォルトとなります。

教えていただいたAppleのデモでもそうなっていました。

昨日のコードを直すとこんな感じになります。

インスタンス変数は

    NSMutableString *_currentParsedCharacterData;
    BOOL _inElement;

実装部は

NSString *kXMLParseElementTag = @"ele";

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
    if ([elementName isEqualToString:kXMLParseElementTag]){
        _inElement = YES;
        [_currentParsedCharacterData setString:@""];
    }
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    if ([elementName isEqualToString:kXMLParseElementTag]){
        _inElement = NO;

        //_currentParsedCharacterDataを使って何かする
    }
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    if (_inElement){
        [_currentParsedCharacterData appendString:string];
    }
}
LINEで送る
Pocket

[iOS] NSXMLParser で文字列が1つのエレメントで複数に分かれてパースされる

2012/07/20

2012/07/22
追記エントリです
>> [iOS] NSXMLParser。SAXとDOMの比較とか

以下もと記事です。
—-
こんにちは。きんくまです。

NSXMLParserを使って、XMLをパースしていたところ、こんな問題が出ました。

例)こんな文字列があったとして

<ele>abc あいう</ele>

parser: foundCharacters:
というので文字列を取得したときに、本来は1回で
「abc あいう」という文字列が欲しいのだけれど、実際は

1回目
abc

2回目
あいう

と2回に分けて文字列がひっぱられてしまっていた。自分の場合は

英数字+マルチバイト

の場合がこうなってしまうみたい。

マルチバイト+英数字

の順番だと問題なく1回だけで文字列が取得できた。

解決方法

調べてみたけど、特にパース用のフラグとかがなさそうだったので手動でやることにした。
パースのカウントが2回以上だったら、仮に保存しておいたものと連結というふうにした。

もし英数字+日本語をパースするんだったらいつもこういうことやっとかないといけないかも。

インスタンス変数を用意

    int _elementParseCount; //追加
    NSString *_lastParsedString; //追加
    BOOL inEle; //名前は適当です

実装側.mで

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
    _elementParseCount = 0;
    _lastParsedString = nil;
    if([elementName isEqualToString:@"ele"]){
        inEle = YES;
    }
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    if([elementName isEqualToString:@"ele"]){
        inEle = NO;
        //_lastParsedStringを使って本来やりたいことをする
    }
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    if(inEle){
        if(_elementParseCount > 0){
            string = [NSString stringWithFormat:@"%@%@", _lastParsedString, string];
        }
        _lastParsedString = string;
        _elementParseCount++;
    }
}
LINEで送る
Pocket

[iOS] 秋ぐらいに出る?らしいXcode4.5でコードを少し短くかけるみたい

2012/07/17

2012/07/27追記
記事のつづきを書きました
>> [iOS] iOS5以下 + Xcode4.4でもObjective-Cの新シンタックスで配列、辞書の値を取得したい

以下、元の記事です

こんにちは。きんくまです。

この間本屋でパラパラと立ち読みしてたら、もうすぐObjective-Cで短縮系の書き方がいくつかできるようになるらしいと書いてあった。

たしかこの本のコラムだったような。ちゃんと見てないので違ってたらスミマセン。
>> 楽しみながら作ってみよう! はじめてのiPhoneアプリプログラミング

調べてみると、具体的なコードとしてはここのページに書いてあります。

>> Objective-C Literals

上のページからいくつか抜き出してみます。

数値だと

NSNumber *fortyTwo = @42; //これを書くのと同じ[NSNumber numberWithInt:42]
NSNumber *piFloat = @3.141592654F;   //これと同じ [NSNumber numberWithFloat:3.141592654F]
NSNumber *yesNumber = @YES;           //これと同じ [NSNumber numberWithBool:YES]

文字列のNSString *name = @”Taro” みたいな感じでやっておけば、
あとはLLVMコンパイラーくんが勝手にいい感じに変換してくれるみたいです。

これは便利です!

配列と辞書の定義

NSArray *array = @[ @"Hello", NSApp, [NSNumber numberWithInt:42] ];
NSDictionary *dictionary = @{
    @"name" : NSUserName(),
    @"date" : [NSDate date],
    @"processInfo" : [NSProcessInfo processInfo]
};

配列と辞書にアクセス

NSMutableArray *array = ...;
NSUInteger idx = ...;
id newObject = ...;
id oldObject = array[idx];
array[idx] = newObject;		    // replace oldObject with newObject

NSMutableDictionary *dictionary = ...;
NSString *key = ...;
oldObject = dictionary[key];
dictionary[key] = newObject;	// replace oldObject with newObject

objectAtIndexとかobjectForKeyとか書かなくてよくなる!
こう書けるとAS, JSとほぼ同じ感覚になるので、良い感じ。
特に、デフォルトは辞書の宣言がありえないぐらい長いっていうか、メンドクサイ書き方なのでありがたいです。

AS / JSとか、言語としての孫世代っていうか、後の世代の言語を書いているとその便利さに気がつかなかったのですが、
自分で書いているコードの裏では実際にはこういうことを自動でやってくれてるんだなあと思いました。

それで、じゃあこれいつからこうやって書けるようになるのよ?ということを調べてみると、
秋に出るらしいiOS6とセットのXcode4.5からみたいです。

>> Using new Objective-C literals

ただXcode4.5はまだプレビュー版で、Dev Centerでダウンロードして試すことは可能なのですが、それを使ってアプリの申請はできないです。
なので、秋まで気長に待ちます。

LINEで送る
Pocket

[日記] F-siteで話します

2012/07/6

こんにちは。きんくまです。

2年くらい前からF-siteのスタッフをやっています。
>> F-site

F-siteというのは簡単にいうと、Flashの勉強会やったり、Flashまわりの情報などを提供する団体です。
勉強会の内容は様々で、技術的なこととか、制作事例だったりします。
個人的には、作った人が何を考えてたかとか、どういうところが大変だったかを聞ける事例紹介が楽しみだったりします。
あ、でもAdobeとは資本関係とか全くなく、純粋なユーザーコミュニティというかボランティアというか、そんな感じです。

私はときどき、受付にいたりするので、若白髪のおっさんがいたら私だと思って大丈夫です。

そんで、今月末にやる回は技術よりの回です。
その中のFlashの新機能を紹介する役目をもらったのでお知らせです。
demo1でAIR関連の部分を担当します。

>> Flash Professional CS6がやって来た ヤア!ヤア!ヤア!

今年の2月のときも話す予定があったのですが、父のことがありできなかったので、今回は頑張りますです。
>> node.jsを使ってflashにも描き込むお絵描きチャット

CreatreJS, ANEなど面白そうなネタもあるので、興味のある方はぜひお越し下さいませ。

LINEで送る
Pocket

[iOS] チェックボックスをつけたUITableViewCellをつくる

2012/07/1

こんにちは。きんくまです。

チェックボックスをつけたUITableViewCellを作るのにちょっとハマったのでメモです。

checkbox_cap1

UITableViewCellのサブクラスでチェックボックスを作るところまではいいのですが、
このセルには情報を保存しておけないのでした。

UITableViewDataSourceの– tableView:cellForRowAtIndexPath: で画面から見えなくなったセルを破棄したり、これから見えるセルを自動作成してくれます。
それでセルを作成する際に、dequeueReusableCellWithIdentifierでキャッシュされているセルを使う場合があります。

このキャッシュがくせ者で、サブクラスの中に情報を埋め込んでいても、予想とは違うキャッシュされたセルがひっぱられることがあります。
例えばindexPathのデータをセルにいれても、セルとindexPathが1対1に対応されないとか。
たくさんの情報を入れてスクロールしてみるとよくわかりますです。

で調べたところApple のサンプルコードが出てきました。
>> Accessory
(何故か昨日までは見られたのに、今は見られないという、、)

いろいろと考えてみたところ、ポイントとしては、

1) セルの方には情報をもたせず、親の方にもたせる
2) UITableViewのindexPathForRowAtPoint: を使ってタッチされたセルを特定
3) tableView:cellForRowAtIndexPath:のときに保存しておいたデータをもとにセルの見た目を設定する

という感じでしょうか。

これをふまえて、作成したのが以下のコードです。
参考までにプロジェクトデータもアップしました。

※2012/07/22 delegateのプロパティ属性を修正しました
>> CellWithCheckbox

KKTableViewController.h

#import <UIKit/UIKit.h>
#import "KKCellWithCheckbox.h"

@interface KKTableViewController : UITableViewController<KKCellWithCheckboxDelegate>
{
    NSArray *_cellDataArray;
}

@end

KKTableViewController.m

#import "KKTableViewController.h"
#import "KKCellWithCheckbox.h"

@interface KKCellData:NSObject {
    NSString *_cellName;
    BOOL _isChecked;
}
@property(nonatomic, retain) NSString *cellName;
@property(nonatomic, assign) BOOL isChecked;
@end

@implementation KKCellData
@synthesize cellName = _cellName;
@synthesize isChecked = _isChecked;

- (void)dealloc
{
    [_cellName release];
    [super dealloc];
}
@end



@interface KKTableViewController ()

@end

@implementation KKTableViewController

- (id)init
{
    self = [super init];
    if (self){
        NSMutableArray *tmpArr = [[NSMutableArray alloc] init];
        for (int i = 0; i < 100; i++){
            NSString *cellName = [NSString stringWithFormat:@"row %d", i];
            KKCellData *cellData = [[KKCellData alloc] init];
            cellData.cellName = cellName;
            cellData.isChecked = NO;
            [tmpArr addObject:cellData];
            [cellData release];
        }
        _cellDataArray = [[NSArray alloc] initWithArray:tmpArr];
        [tmpArr release];
    }
    return self;
}

- (void)dealloc
{
    [_cellDataArray release];
    [super dealloc];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"KKTableViewController";
    KKCellWithCheckbox *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if(cell == nil){
        cell = [[[KKCellWithCheckbox alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
    }
    cell.delegate = self;
    [cell setCheckboxState:[(KKCellData *)[_cellDataArray objectAtIndex:indexPath.row] isChecked]];
    KKCellData *cellData = [_cellDataArray objectAtIndex:indexPath.row];
    [cell setCellName:cellData.cellName];

    return cell;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [_cellDataArray count];
}

- (void)cell:(KKCellWithCheckbox *)cell checkboxTappedEvent:(UITouch *)touch
{
    CGPoint touchPt = [touch locationInView:self.view];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:touchPt];
    KKCellData *cellData = [_cellDataArray objectAtIndex:indexPath.row];
    cellData.isChecked = !cellData.isChecked;
    [cell setCheckboxState:cellData.isChecked];
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"row %d is selected", indexPath.row);
}

@end

KKCellWithCheckbox.h

#import <UIKit/UIKit.h>

@class KKCellWithCheckbox;

@protocol KKCellWithCheckboxDelegate<NSObject>
@optional
-(void)cell:(KKCellWithCheckbox *)cell checkboxTappedEvent:(UITouch *)touch;
@end

@interface KKCellWithCheckbox : UITableViewCell{
    UILabel *_cellNameLabel;
    UIButton *_checkboxButton;
    UIImage *_checkedImage;
    UIImage *_uncheckedImage;
    id<KKCellWithCheckboxDelegate> _delegate;
}
@property(nonatomic, assign) id <KKCellWithCheckboxDelegate> delegate;
- (void)setCellName:(NSString *)aCellName;
- (void)setCheckboxState:(BOOL)isCheck;
@end

KKCellWithCheckbox.m


#import "KKCellWithCheckbox.h"

@interface KKCellWithCheckbox()
- (void)checkboxTapped:(id)sender event:(id)event;
@end

@implementation KKCellWithCheckbox
@synthesize delegate = _delegate;

- (void)dealloc
{
    [_cellNameLabel release];
    [_checkedImage release];
    [_uncheckedImage release];
    [_checkboxButton release];
    [super dealloc];
}

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        _checkboxButton = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
        NSString *checkedImgPath = [[NSBundle mainBundle] pathForResource:@"checkedmark" ofType:@"png"];
        NSString *uncheckedImgPath = [[NSBundle mainBundle] pathForResource:@"uncheckmark" ofType:@"png"];
        _checkedImage = [[UIImage alloc] initWithContentsOfFile:checkedImgPath];
        _uncheckedImage = [[UIImage alloc] initWithContentsOfFile:uncheckedImgPath];
        [_checkboxButton setImage:_uncheckedImage forState:UIControlStateNormal];
        _checkboxButton.frame = CGRectMake(0, 0, 44, 44);
        [_checkboxButton addTarget:self
                            action:@selector(checkboxTapped:event:)
                  forControlEvents:UIControlEventTouchUpInside];
        [self addSubview:_checkboxButton];

        _cellNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(60, 11, 250, 20)];
        _cellNameLabel.font = [UIFont fontWithName:@"HelveticaNeue" size:16];
        [self addSubview:_cellNameLabel];
    }
    return self;
}

- (void)setCellName:(NSString *)aCellName
{
    _cellNameLabel.text = aCellName;
}

- (void)checkboxTapped:(id)sender event:(id)event
{
    NSSet *set = [event allTouches];
    UITouch *touch = [set anyObject];
    if (touch && [_delegate respondsToSelector:@selector(cell:checkboxTappedEvent:)]){
        [_delegate cell:self checkboxTappedEvent:touch];
    }
}

- (void)setCheckboxState:(BOOL)isCheck
{
    if(isCheck){
        [_checkboxButton setImage:_checkedImage forState:UIControlStateNormal];
    }else{
        [_checkboxButton setImage:_uncheckedImage forState:UIControlStateNormal];
    }
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];
    if (selected){
        _cellNameLabel.textColor = [UIColor whiteColor];
    }else{
        _cellNameLabel.textColor = [UIColor blackColor];
    }
}

@end
LINEで送る
Pocket

[日記] イラストとか

2012/06/30

こんにちは。きんくまです。
今回は趣味の話です。

昨年ぐらいから深夜アニメを見るようになった影響で、
今年の春ごろから、自分でも萌えキャラ描いてみたいなーと思い、やってみてます。

miku1206s

前にイラストを少し描いていたので、やる前は少しは描けるかなーと思っていたのですが、
実際やってみるとすごく難しいですね。

そもそも描いた絵が萌えないw のもあるんですけど、人物そのものがムズイ。
手やらポーズやら。

あとなにせ描いてるのがおっさんなので、出来た絵を自分で見て古くさく感じられたりして、
こりゃどうすりゃ今風になるんだ? 色使いか? エフェクトなのか?
なんてことを考えながら試行錯誤してたりします。
そこが楽しいのですけどね。

イラストレーターさんの書き込まれたような絵も良いのですが、
自分はアニメーターさんのようなすっきりした線が好みですね。
特に躍動感のあるやつとか。

去年から子供と日曜にやってるプリキュアを見るようなったのですけど、
プリキュア調べてたらキャラデザをしてた馬越さんという方の本を買っていたという。

>> 馬越嘉彦 東映アニメーションワークス

馬越さんは自分の見始める1年前のハートキャッチの方だったんで、本編の動いているのは残念ながら見たことがないです。
でもまあ、この本を見てると本当に人物や線がいきいきと描かれていて、すばらしいです。

LINEで送る
Pocket

ページトップへ戻る