25
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

iOSのスクリーンで2本指を使いUIViewの回転、拡大/縮小を同時に行う

Last updated at Posted at 2014-02-01

2本指を使って、Viewの回転、拡大を同時に行うことができるサンプルです。
スタンプカメラなどに活用できるかと思います。

UIPinchGestureRecognizerとUIRotationGestureRecognizerを使用しててピンチイン/アウトもしくは回転を行うことはできますが、同時にこれら両方のジェスチャーを認識することはできないようです。

しかしながら、

  • (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
    を用いることでViewの回転、拡大を同時に行うことができます。

以下、実装方法です。
まず、UIViewを継承した変形可能なViewのクラスを作成します。

TransformableView.h
#import <UIKit/UIKit.h>

@interface TransformableView : UIView

@property(nonatomic) CGFloat scale;
@property(nonatomic) CGFloat angle;
@property(nonatomic) BOOL isTransformable;

@end
TransformableView.m
#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の中に、以下の記述をします。

ViewController.m
#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

25
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?