UIScrollViewの中身を回転させる

HMDTさんのこの記事を読んで。

http://hmdt.jp/archives/2010_07.html#2010070801
iPhone SDKが登場したときから、ずっとやろうとしてなかなかうまくいかなかったものに、拡大縮小および回転可能な画像の表示がある。ピンチイン/アウトで拡大縮小して、あと画像の回転もできるやつ。標準の写真アプリみたいなやつね。それっぽい動作をするものならなんとかできるんだけど、完璧なものをやろうとすると、とても難しい。
UIScrollViewを使って実現する訳だ。デリゲートを使って、zoomの対象となるビューを指定してやればいい。単純にズームするだけならいいんだけど、これにcontentOffsetやcontentSizeの変更とか、ビューのtransformの変更とか加えると、とたんにUIScrollViewが言うことをきかなくなる。原因が分からなくて、ずっとイライラさせられてきた。
もういい加減長年の関係にケリをつけたくて、時間を割いて徹底的に検討してみた。回転のために変更したtransformプロパティの値をいろんなタイミングで表示させてみると、なんか見たことない値入ってるー。

で、私の作ったRGBTouchっていうアプリはまさに同じことをしている。UIScrollViewにUIImageViewを入れて、ピンチで拡大縮小、ボタンで回転(とある理由でセンサーで回転にはしていない)してる。で、UIImageViewのtransformがUIScrollViewによって変えられていることには気付いた。私の対処法は、UIScrollViewによってズームの値が設定されているtransformに、さらに回転を加えてUIImageViewに設定し直す、ということだった。コードはこんなん。

-(IBAction)rotateImage:(id)sender {
	if (angle >= 3) angle = 0;
	else angle++;
	
	CGSize testSize = CGSizeApplyAffineTransform(CGSizeMake(0.5, 0.5), imageView.transform);
	CGFloat scale = fabs(testSize.width) + fabs(testSize.height);
	
	CGAffineTransform newTransform = CGAffineTransformMakeRotation(M_PI / 2.0 * angle);
	newTransform = CGAffineTransformScale(newTransform, scale, scale);
	
	CGSize size = image.size;
	
	CGSize transformedSize = CGSizeApplyAffineTransform(size, newTransform);
	transformedSize.width = fabs(transformedSize.width);
	transformedSize.height = fabs(transformedSize.height);
	
	[UIView beginAnimations:nil context:nil];
	
	scrollView.contentSize = transformedSize;
	imageView.transform = newTransform;
	imageView.center = CGPointMake(transformedSize.width/2.0, transformedSize.height/2.0);
		
	[UIView commitAnimations];
}

補足説明。

  • imageViewの初期サイズはimageと同じにしてある。
  • scrollViewのzoomScaleは使っていないのは、この方法を繰り返すと正しい値を返してくれなくなるため。
  • imageViewのframeはいじらない。transformをいじるとframeは使えなくなってしまうとリファレンスに書いてある。代わりにcenterを使う。