今回はCanvasView側アプリの作成。
カスタムUIViewであるCanvasViewで受け取ったタッチイベントに合わせて事前に用意しておいた画面外描画領域(オフスクリーンというのじゃよ)に線を描画し、このオフスクリーンをCanvasViewで逐次描画してやればお絵描きソフトができるわけざんす。
カスタムUIViewの作り方はドリル:画面へのタッチを検出する(1)を参照ね。
でもって、カスタムUIViewの内容部描画方法はドリル:カスタムUIViewの内容部を独自に描画するを参考にして下さい。
ただ~し、上のドリルだとオフスクリーンをどうやって画面に描画するかはわからない。
オフスクリーンの作り方や、CGContextRefへの描画方法は「その(175)」を読むとして、ここで使ってるCGContextDrawImageを実行するために必要なラストワンピース、CGContextRefを取り出す方法が書かれてないんだよね。
そいつが
なんですが、ここらへんはずっと前にやった「その(58)」を読みましょう。
実装はこんな感じ。
ここで出てくるcanvasContextてのがオフスクリーンです。
CanvasViewのインスタンス変数として定義しておきます。
でもって、CanvasViewの初期化時に以下のように作成して緑一色に塗りつぶしておきます。
createCanvasContext関数は特に説明しません。サンプルソース見てね。
指先が触れて、そのまま引っ張ると線が引かれる流れはこんな感じ。
まず、CanvasViewにタッチの通知(touchesBeganメソッドが呼び出される)。
CanvasViewはこのタッチ位置を線を描画するさいの開始位置として記憶。
こいつはドラッグ時に呼び出されるtouchesMovedメソッドでも使うことになるのでlineStartPosとしてCanvasViewのインスタンス変数として宣言。
このtouchesMovedメソッドはドラッグ中は指の位置が変わるたびに呼び出されます。
CanvasViewはそのつどtouchesBeganで記憶した線描画開始位置lineStartPosから、通知された位置までの線を引くわけですわ。
ここで、線を引いた後は通知された位置を新しく線描画開始位置として設定することで、次のtouchesMovedメソッドで引くラインは前回のラインの終端からって事になるんですな。
で、まあこんな感じでオフスクリーンを更新したら
ってやることでdrawRectメソッドが呼び出されます。これで更新されたオフスクリーンが画面上に描画されるわけです。
これを指が放された通知(touchesEnded/touchesCancelledメソッドの呼び出し)が来るまで繰り返す。
こうすることでオフスクリーン上に、どんどんラインが積み重なるわけですな。ペイント系(Photoshopとか)といわれるアプリケーションのやり方です。
オフスクリーンを使わずに、タッチやドラッグ時の指の座標だけ記憶しておいて、drawRectではこの座標を元にラインを描く事もできるます。この場合、座標から毎回ラインを描くので、後からラインの太さや色を変えることも出来る。
ドロー系(イラストレータとか)といわれるアプリケーションのやり方です。
今回はペイント系でいきます。
と、ここまではCanvasViewをUIScrollViewに埋め込んでない場合の話。
埋め込まれて何が問題になるかというとUIScrollViewが先にドラッグに反応しちゃうってことです。
で、UIScrollViewが先にドラッグに反応する問題の回避方法ですが、とりあえずCanvasViewController側で
とすることで、タッチした後、すぐに動かさずにいったん間をあけてから動かすと線が描けるようにはなってます。このネタは「その(123)」から。
でも、とっても使いにくいのねん。
イライラする。
私!イライラする。
一応、ピンチで縮小拡大もできる
なんとか、ここらへんを解決せねば...
まて、次回!
------------
サンプルプロジェクト:Canvas.zip
カスタムUIViewであるCanvasViewで受け取ったタッチイベントに合わせて事前に用意しておいた画面外描画領域(オフスクリーンというのじゃよ)に線を描画し、このオフスクリーンをCanvasViewで逐次描画してやればお絵描きソフトができるわけざんす。
カスタムUIViewの作り方はドリル:画面へのタッチを検出する(1)を参照ね。
でもって、カスタムUIViewの内容部描画方法はドリル:カスタムUIViewの内容部を独自に描画するを参考にして下さい。
ただ~し、上のドリルだとオフスクリーンをどうやって画面に描画するかはわからない。
オフスクリーンの作り方や、CGContextRefへの描画方法は「その(175)」を読むとして、ここで使ってるCGContextDrawImageを実行するために必要なラストワンピース、CGContextRefを取り出す方法が書かれてないんだよね。
そいつが
CGContextRef context = UIGraphicsGetCurrentContext() ;
なんですが、ここらへんはずっと前にやった「その(58)」を読みましょう。
実装はこんな感じ。
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext() ;
CGImageRef imgRef = CGBitmapContextCreateImage(canvasContext);
CGRect r = self.bounds;
CGContextDrawImage(context, CGRectMake(0, 0, r.size.width, r.size.height), imgRef);
CGImageRelease(imgRef);
}
CGContextRef context = UIGraphicsGetCurrentContext() ;
CGImageRef imgRef = CGBitmapContextCreateImage(canvasContext);
CGRect r = self.bounds;
CGContextDrawImage(context, CGRectMake(0, 0, r.size.width, r.size.height), imgRef);
CGImageRelease(imgRef);
}
ここで出てくるcanvasContextてのがオフスクリーンです。
CanvasViewのインスタンス変数として定義しておきます。
@interface CanvasView : UIView {
CGContextRef canvasContext;
}
@end
CGContextRef canvasContext;
}
@end
でもって、CanvasViewの初期化時に以下のように作成して緑一色に塗りつぶしておきます。
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
canvasContext = createCanvasContext(
frame.size.width, frame.size.height);
CGContextSetRGBFillColor(canvasContext, 0, 1, 0, 1);
CGContextFillRect(canvasContext,
CGRectMake(0, 0, frame.size.width, frame.size.height));
}
return self;
}
if (self = [super initWithFrame:frame]) {
canvasContext = createCanvasContext(
frame.size.width, frame.size.height);
CGContextSetRGBFillColor(canvasContext, 0, 1, 0, 1);
CGContextFillRect(canvasContext,
CGRectMake(0, 0, frame.size.width, frame.size.height));
}
return self;
}
createCanvasContext関数は特に説明しません。サンプルソース見てね。
指先が触れて、そのまま引っ張ると線が引かれる流れはこんな感じ。
まず、CanvasViewにタッチの通知(touchesBeganメソッドが呼び出される)。
CanvasViewはこのタッチ位置を線を描画するさいの開始位置として記憶。
こいつはドラッグ時に呼び出されるtouchesMovedメソッドでも使うことになるのでlineStartPosとしてCanvasViewのインスタンス変数として宣言。
@interface CanvasView : UIView {
CGContextRef canvasContext;
CGPoint lineStartPos;
}
@end
CGContextRef canvasContext;
CGPoint lineStartPos;
}
@end
このtouchesMovedメソッドはドラッグ中は指の位置が変わるたびに呼び出されます。
CanvasViewはそのつどtouchesBeganで記憶した線描画開始位置lineStartPosから、通知された位置までの線を引くわけですわ。
ここで、線を引いた後は通知された位置を新しく線描画開始位置として設定することで、次のtouchesMovedメソッドで引くラインは前回のラインの終端からって事になるんですな。
で、まあこんな感じでオフスクリーンを更新したら
[self setNeedsDisplay];
ってやることでdrawRectメソッドが呼び出されます。これで更新されたオフスクリーンが画面上に描画されるわけです。
これを指が放された通知(touchesEnded/touchesCancelledメソッドの呼び出し)が来るまで繰り返す。
こうすることでオフスクリーン上に、どんどんラインが積み重なるわけですな。ペイント系(Photoshopとか)といわれるアプリケーションのやり方です。
オフスクリーンを使わずに、タッチやドラッグ時の指の座標だけ記憶しておいて、drawRectではこの座標を元にラインを描く事もできるます。この場合、座標から毎回ラインを描くので、後からラインの太さや色を変えることも出来る。
ドロー系(イラストレータとか)といわれるアプリケーションのやり方です。
今回はペイント系でいきます。
と、ここまではCanvasViewをUIScrollViewに埋め込んでない場合の話。
埋め込まれて何が問題になるかというとUIScrollViewが先にドラッグに反応しちゃうってことです。
CanvasViewをUIScrollViewに埋め込む作業は「iPhoneアプリ開発、その(118)」をみてね。
「その(164)」から、loadView、viewDidLoadメソッド、どっちでの実装でもいいです。
今回はviewDidLoadメソッド側にしてみた。
「その(164)」から、loadView、viewDidLoadメソッド、どっちでの実装でもいいです。
今回はviewDidLoadメソッド側にしてみた。
で、UIScrollViewが先にドラッグに反応する問題の回避方法ですが、とりあえずCanvasViewController側で
[scrollview setCanCancelContentTouches:NO];
とすることで、タッチした後、すぐに動かさずにいったん間をあけてから動かすと線が描けるようにはなってます。このネタは「その(123)」から。
でも、とっても使いにくいのねん。
イライラする。
私!イライラする。
一応、ピンチで縮小拡大もできる
なんとか、ここらへんを解決せねば...
まて、次回!
------------
サンプルプロジェクト:Canvas.zip