2本指を使って、Viewの回転、拡大を同時に行うことができるサンプルです。
スタンプカメラなどに活用できるかと思います。
UIPinchGestureRecognizerとUIRotationGestureRecognizerを使用しててピンチイン/アウトもしくは回転を行うことはできますが、同時にこれら両方のジェスチャーを認識することはできないようです。
しかしながら、
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
を用いることでViewの回転、拡大を同時に行うことができます。
以下、実装方法です。
まず、UIViewを継承した変形可能なViewのクラスを作成します。
#import <UIKit/UIKit.h>
@interface TransformableView : UIView
@property(nonatomic) CGFloat scale;
@property(nonatomic) CGFloat angle;
@property(nonatomic) BOOL isTransformable;
@end
#import "TransformableView.h"
@implementation TransformableView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
_scale = 1.0;
_angle = 0.0;
_isTransformable = YES;
}
return self;
}
-(void)setScale:(CGFloat)scale{
if (!_isTransformable) {
return;
}
//Minimum scale
if (scale<0.5) {
scale = 0.5;
}
//Max scale
if (scale>2.0) {
scale = 2.0;
}
_scale = scale;
[self doTransform];
}
-(void)setAngle:(CGFloat)angle{
if (!_isTransformable) {
return;
}
_angle = angle;
[self doTransform];
}
-(void)doTransform{
CGAffineTransform pinchTransform = CGAffineTransformMakeScale(_scale, _scale);
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(_angle);
self.transform = CGAffineTransformConcat(pinchTransform, rotationTransform);
}
@end
スケールと角度のセッターをオーバーライドしてそれぞれから変形を行うメソッドを呼び出しています。
そして、このViewを配置するView Controllerの中に、以下の記述をします。
#import "ViewController.h"
#import "TransformableView.h"
@interface ViewController (){
TransformableView *tView;
}
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//Multiple touch
self.view.multipleTouchEnabled = YES;
//New Transformable View
tView = [TransformableView new];
tView.backgroundColor = [UIColor redColor];
tView.isTransformable = YES;
tView.frame = CGRectMake(0, 0, 100, 100);
tView.center = CGPointMake(self.view.frame.size.width/2, self.view.frame.size.height/2);
[self.view addSubview:tView];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if ([touches count] == 2) {
//Touches
NSArray *touchesArray = [touches allObjects];
UITouch *touch1 = touchesArray[0];
UITouch *touch2 = touchesArray[1];
//Point 1
CGPoint prePoint1 = [touch1 previousLocationInView:self.view];
CGPoint locationPoint1 = [touch1 locationInView:self.view];
//Point2
CGPoint prePoint2 = [touch2 previousLocationInView:self.view];
CGPoint locationPoint2 = [touch2 locationInView:self.view];
//Distance
CGFloat preDistance = sqrtf(powf(prePoint2.x-prePoint1.x, 2)+
powf(prePoint2.y-prePoint1.y, 2));
CGFloat locationDistance = sqrtf(powf(locationPoint2.x-locationPoint1.x, 2)+
powf(locationPoint2.y-locationPoint1.y, 2));
//Scale increment
tView.scale *= locationDistance/preDistance;
//Angle increment
CGFloat angleIncrement = angleBetweenLinesInRadians([touch1 previousLocationInView:self.view],
[touch2 previousLocationInView:self.view],
[touch1 locationInView:self.view],
[touch2 locationInView:self.view]);
tView.angle += angleIncrement;
}
}
static inline CGFloat angleBetweenLinesInRadians(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) {
CGFloat a = line1End.x - line1Start.x;
CGFloat b = line1End.y - line1Start.y;
CGFloat c = line2End.x - line2Start.x;
CGFloat d = line2End.y - line2Start.y;
CGFloat line1Slope = (line1End.y - line1Start.y) / (line1End.x - line1Start.x);
CGFloat line2Slope = (line2End.y - line2Start.y) / (line2End.x - line2Start.x);
CGFloat degs = acosf(((a*c) + (b*d)) / ((sqrt(a*a + b*b)) * (sqrt(c*c + d*d))));
return (line2Slope > line1Slope) ? degs : -degs;
}
@end
2本指の間の距離の変化率から、Viewのスケールの変化率を計算し、
2本指を結んだ直線の傾きの変化から、Viewの角度の変化率を計算しています。
傾きの計算方法に関しては、以下の記事を参照しました。
http://stackoverflow.com/questions/7863265/swipe-detection-in-any-angle
興味のある方は、ぜひスクリーンに2本指を置いて試してみてくださいね。
シミュレータの場合は、optionを押しながら画面をクリックすると2本指をシミュレートできます。
サンプルコードをGitHubに置いておきますね。
https://github.com/yukinaga/PinchRotationSample