がっ・たい。

 まずはPaintってフォルダ作ってその中に、Canvas03とColor05フォルダを移動。CanvasとColorに戻します。特に名前を戻す必要は無いんだけどね。一応。

$テン*シー*シー-1

 配置したらCanvas.xcodeproをXcodeで開き、プロジェクト>新規グループメニューで
Pen
グループを作成、そのままcolor.xcodeprojも開いて
PenColorView.h
PenColorView.m
PenSizeView.h
PenSizeView.m
colorViewController.h
colorViewController.m
を、さっき作ったPenグループにドラッグ&ドロップ。
あと、
eraser.png
pen.png
も同じくPenグループにドラッグ&ドロップ。

$テン*シー*シー-2

 これで、Canvas.xcodeproでcolorViewControllerを使う準備ができたわけっす。ええええ?colorViewController.xib無視っすか?と思った人はその(164)その(167)の激闘の記録を読むべし!
 あとはCanvasViewController.mで
#import "colorViewController.h"
としてviewDidLoadメソッドに
colorViewController* penController = [[colorViewController alloc] init];
[self.view addSubview:penController.view];
として軽く実行してみたんすよ。
 本来penControllerはインスタンス変数にしてdeallocメソッドでreleaseしないと駄目だけど、とりあえずということで。実行!

$テン*シー*シー-3

 ごらんの有り様だよ。
 まあ、動くんすけどね。
 めり込んじゃってます。

 ならば~ぁぁあ!
 と
colorViewController* penController = [[colorViewController alloc] init];
[self.view.window addSubview:penController.view];

 なんてふうに、viewが所属してるwindowにaddViewして実行してみたんすよ。

$テン*シー*シー-4

 なにー!消えたあああ
 ですね…
 ですよ。

 ふっ。
 よくよく考えたら、viewDidLoadメソッドの段階じゃはself.view.windowはnilやっちゅーねん。その(164)読めとか言いつつ、自分はすっかり忘れてたわ。
 viewDidLoadメソッドが呼ばれるタイミングがCanvasAppDelegateの
[window addSubview:viewController.view];

 のviewController.viewなわけだからさ~、
 まだwindowにaddSubviewされてない状態なんよね~。

 んじゃself.view.windowがnilじゃなくなるタイミングはというと画面上に表示された後に呼ばれる
- (void)viewDidAppear:(BOOL)animated

 なわけですよ。
 というわけで、最終的に実装したのが
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
colorViewController* penController = [[colorViewController alloc] init];
[self.view.window addSubview:penController.view];
}

 ばっちりです。

$テン*シー*シー-5

 ただ~し、CanvasとcolorViewControllerのビューが重なっちゃってるんで、こいつを動かします。
double height = penController.view.bounds.size.height;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
CGRect r = scrollview.frame;
r.size.height -= height;
scrollview.frame = r;
[UIView commitAnimations];

 アニメーション化したのは単なる趣味。
 サンプルのこの部分をsetAnimationDurationの値を1.0以外に設定して試してみると面白いよ。
 ま、setAnimationDurationはcolorViewControllerもCanvasViewControllerも最終的に削除の方向が正解ですな。
 でないと、両方のビューをシンクロさせるためには、colorViewControllerに設定されたDuration値を得る機能を追加しないとならなくなる。デフォルトに任せるのがいいでしょ。
 こんな感じでCavasViewとcolorViewControllerのビューの重なりは解除。

$テン*シー*シー-6

 で~、いよいよcolorViewControllerからのdelegateでCavasViewのペンサイズや色を変えていくわけなんですが、そのためにCavasViewクラスに以下のインスタンス変数を用意します。
UIColor* penColor; // ペンの色
double penWidth; // ペンの幅
BOOL eraseMode; // 消しゴムにする場合YES


@property (nonatomic, retain) UIColor* penColor;
@property (nonatomic, assign) double penWidth;
@property (nonatomic, assign) BOOL eraseMode;

 今回は@propertyを使ってドット構文で設定できるようにもします。property?な人は「propertyをちょっと調べてみた」を読んでチョ。 ドット構文はそのものずばり「Objective-C 2.0プログラミング言語: ドット構文」ね。
 実装側ではtouchesMoved:withEvent:メソッドを、ちょろっと変更。penWidthは0.0~1.0なんでね
、消しゴムは大きめに40倍、ペンの方は20倍してます。+1てのは0の時のことを考えて。ここらへんかなり適当。


if (c == 1)
mode = eraseMode?mode_Eraser:mode_Pen;
else if (c == 3)
mode = mode_Eraser;


if ((mode == mode_Pen) || (mode == mode_Eraser)) {
CGPoint lineEndPos = [touch locationInView:self];
CGContextMoveToPoint(canvasContext, lineStartPos.x, lineStartPos.y);
CGContextAddLineToPoint(canvasContext, lineEndPos.x, lineEndPos.y);
if (mode == mode_Eraser) {
CGContextSetLineWidth(canvasContext, penWidth * 40.0 + 1);
CGContextSetRGBStrokeColor(canvasContext, 1.0, 1.0, 1.0, 1.0);
} else {
CGContextSetLineWidth(canvasContext, penWidth * 20.0 + 1);
const CGFloat* components
= CGColorGetComponents(penColor.CGColor);
CGContextSetRGBStrokeColor(canvasContext,
components[0], components[1], components[2], 1.0);

}


 そろそろお絵描きアプリっぽく背景は白にしてます。
 
 受け手側はこんな感じでいいとして、colorViewControllerからどうやってユーザが選んだペンの色やサイズを伝えるかですが…
 当然delegate定義します。colorViewController.hに
@protocol colorViewControllerDelegate
// ペン幅が変更された。
-(void)setPenWidth:(double)inWidth;
// ペン色が変更された。
-(void)setPenColor:(UIColor*)inColor;
// ペン/消しゴムの切り替えが発生した。
-(void)setEraseMode:(BOOL)inEraseMode;
@end

 を定義。
 colorViewControllerクラスの実装ではPenSizeviewのdelegateに自身を設定し
-(void)penSizeView:(PenSizeView*)view size:(double)inSize {
if ([penDelegate respondsToSelector:@selector(setPenWidth:)]) {
[penDelegate setPenWidth:inSize];
}
if ([penDelegate respondsToSelector:@selector(setEraseMode:)]) {
[penDelegate setEraseMode:(view == penSizeview)?NO:YES];
}
}

 という処理を追加。
 ここで出ているpenDelegateはcolorViewControllerクラスにインスタンス変数
id<colorViewControllerDelegate, NSObject> penDelegate;
として追加します。
 あと、penColorView:value:側にも
if ([penDelegate respondsToSelector:@selector(setPenColor:)]) {
[penDelegate setPenColor:newcolor];
}
if ([penDelegate respondsToSelector:@selector(setEraseMode:)]) {
[penDelegate setEraseMode:NO];
}

 を追加。
 これで、CanvasViewControllerがcolorViewControllerDelegateを継承すれば連動機構が完成するわけです。
@interface CanvasViewController : UIViewController<
UIScrollViewDelegate, colorViewControllerDelegate> {

 実装側はこんな感じ。
-(void)setPenWidth:(double)inWidth {
canvasview.penWidth = inWidth;
}

-(void)setPenColor:(UIColor*)inColor {
canvasview.penColor = inColor;
}

-(void)setEraseMode:(BOOL)inEraseMode {
canvasview.eraseMode = inEraseMode;
}

 ちなみにviewDidLoadメソッドで
canvasview.penWidth = 1.0;
canvasview.penColor = [UIColor
colorWithHue:0.5 saturation:0.5 brightness: 0.5 alpha:1.0];
canvasview.eraseMode = NO;

 って初期化してますが、本来はcolorViewControllerから値をとるべきですね。
 あとPenSizeViewクラスにもPenColorViewクラス同様touchesBegan:withEvent:メソッドを追加ね。
 これでビルドして実行するとこんな感じ。

$テン*シー*シー-7

 う…
 線がタワシみたいになってる。
 こいつは線のエッジをどうか描くかの設定のせい。
CGContextSetLineCap(canvasContext, kCGLineCapRound);

 で、それなりの絵になります。

$テン*シー*シー-8

 ついでに
 CGContextSetBlendMode(canvasContext, kCGBlendModeDarken);

 やCGContextSetRGBStrokeColorのα値を0.2あたりにすると~

$テン*シー*シー-9

 こんな感じで水彩画っぽくなる。
 いやいやいや~、予想以上に面白いですな。

$テン*シー*シー-10

 こうなると、色をいちいち設定するのが面倒になってくるんだよね。
 最後に使った色を100色くらいストックできるパレットが欲しいね~。
 アンドゥとかも欲しい。

 欲望は果てしなく続く。

 続く。

------------
サンプルプロジェクト:paint00.zip