CALayerとUIScrollViewとタッチイベントと混乱
以前CALayerでお絵かきする記事を書いたんですがそれのちょっと発展版です。
お絵かきに付き物の拡大と縮小を実装するとします。
拡大と縮小をタッチイベントでやるかScrollViewでやるかはともかくtransformで変更すると思います。
実際の絵を大小させると確実に劣化するのでまずやらないと思いますが。
ここに大きな落とし穴が存在します。
transformはframeの大きさを変更させます。そしてタッチイベントで取得するのはboundsの値です。
これをそのままお絵かきに反映させると表示してるのはframeのサイズなので縮小時だと線が遅れて付いてきて拡大時だと勝手に進んでいきます。
boundsの値を参考にして書かれた線をframeにresizeして表示してるので100%で表示してる場合は違和感は無いですが変更すると途端に気持ち悪い動きになります。が、内部的にはこれで正解なのですが!
そう拡大してる意味も縮小してる意味も無いのですよ。
だってboundsサイズは絶えず一定ですからframeサイズ変えようといつも100%のサイズに書いてるのと変わらないわけですから。
ここからがちょっと複雑な説明です。
要は描いてる線はframeサイズに合わせて描けばいいのだと。
取得したcurrentPointの値をscaleの値に合わせて変更します。
currentPoint = CGPointMake(currentPoint.x * scale, currentPoint.y * scale);
これで線を書いて表示していくといい感じに線が描かれていきます。
だがしかし!
これを例えばCALyerを通じて表示すると確実に崩れます。
なぜならsubLayerはboundsの値だからです。このままのscaleサイズだと良いんですが変更すると大変なことに。
なので描いた線は一旦元の値に戻す必要があります。
touchesEnded時に
CGAffineTransform trans = CGAffineTransformMakeScale(1 / scale,1 / sclae); [self.bezierPath applyTransform:trans];
ただしこれは最後に描く線ではなく後でLayerに入れるための線です。最後に描く線はresizeする前の値で描画して下さい。
あくまで100%時の値を取得しなければ50%で書かれた線と200%の時に描くと4倍にしないといけなくなります。要は処理が複雑になるので100%のデフォルト値をとっておくと楽ですよって事です。
じゃScrollViewでscale変わった時どうすれば良いのかという事ですが。
描き直すしかありません。
-(void)drawCanvas{ CALayer *oldLayer = [canvas.layer.sublayers objectAtIndex:0]; CALayer *newLayer = [CALayer layer]; newLayer.frame = oldLayer.bounds; for (NSDictionary *path in displayPath) { UIGraphicsBeginImageContextWithOptions(canvas.frame.size,NO,self.drawScale); // 描画領域に、前回までに描画した画像を、描画します。 UIImage *old = [[UIImage alloc]initWithCGImage:(CGImageRef)newLayer.contents]; [old drawAtPoint:CGPointZero]; UIBezierPath *bez = [UIBezierPath bezierPath]; [bez appendPath:path[@"line"]]; [[UIColor clearColor] setStroke]; [path[@"color"] setFill]; //現在のキャンバスサイズに大きさを合わせてあげます。 CGAffineTransform trans = CGAffineTransformMakeScale(scale,scale); [bez applyTransform:trans]; [bez stroke]; [bez fill]; // 描画した画像をcanvasにセットして、画面に表示します。 newLayer.contents = (id)[UIGraphicsGetImageFromCurrentImageContext() CGImage]; // 描画を終了します。 UIGraphicsEndImageContext(); } [canvas.layer replaceSublayer:[canvas.layer.sublayers objectAtIndex:0] with:newLayer]; }
描いた線の情報をDictionaryの形でpathやcolorの情報をもっています。
これでどんなscaleでも書いた時と同じ条件での描画が出来るようになります。
正直かなり面倒です。
ですが得るものは大きいです!
拡大時に細かい文字が書けます!
まぁあまり拡大すると処理落ちが激しくなるのでiPhoneの進化を待つしかないですけどね。