Search
Write a publication
Pull to refresh

Определение ориентации Pinch жеста

Reading time4 min
Views3.1K

Краткое введение


В одном из проектов мне понадобилось определение ориентации жеста Pinch. Нужно это было для выборочного масштабирования объекта по оси. Т.е. если пальцы сдвигаются/раздвигаются вертикально, то и объект мы масштабируем только вертикально. К сожалению, стандартный распознаватель подобного жеста UIPinchGestureRecognizer не предоставляет такой информации. Просмотрев первую страницу результатов поиска в гугле я не наткнулся на готовое решение. «Ну что же, напишем сами. Так даже интересней :)» — подумал я и приступил к реализации своего пинча.


Редактирование


10.07.2010 — обновлен исходный код. Добавлено свойство oScale(CGPoint). Содержит коэффициент масштабирования по отдельным осям. Аналог scale с UIPinchGestureRecognizer.

Основная разработка


Для определения ориентации пальцев при выполнении Pinch'а нужно решить две подзадачи
  1. Определить сам жест Pinch
  2. Определить угол наклона пальцев

С первой подзадачей проблем не возникло. Прочитав еще раз документацию я удостоверился в том, что все уже сделано давно до нас можно писать свои «распознавалки» жестов на основе UIGestureRecognizer, а следовательно и UIPinchGestureRecognizer.
Решение второй подзадачи тоже не заняло много времени. Немного подумав
image
я нашел решение: просто определять угол между осью Oy экрана и линией, проходящей через пальцы пользователя.

Все, можно было приниматься за написание кода. Ниже результаты работы:

Описание:



//
//  UIOrientedPinchGestureRecognizer.h
//  Бойко А.В.
//


#import <Foundation/Foundation.h>
#import "UIKit/UIGestureRecognizerSubclass.h"

typedef enum{
    OrientationNone             = 0,
    OrientationVertical         = 1,
    OrientationHorizontal       = 2,
    OrientationDiagonal         = 3,
    OrientationInvertedDiagonal = 4
}Orientation;


@interface UIOrientedPinchGestureRecognizer : UIPinchGestureRecognizer {
    Orientation _orientation;
    CGFloat     _angle;
    CGPoint     _lastScale;
    CGFloat     _oldScale;
}
//Ориентация жеста
@property (nonatomic) Orientation orientation;

//Угол жеста, относительно OY. В радианах. От 0 до PI. При angle==PI_2 orientation==OrientationHorizontal
@property (nonatomic) CGFloat     angle;
@property (nonatomic) CGPoint     oScale;
@end

#pragma mark c_func
static inline CGFloat degToRad(CGFloat deg) {return deg*M_PI/180;}
static inline CGFloat radToDeg(CGFloat rad) {return rad*180/M_PI;}
//определение косинуса угла между тремя точками. Определяемый угол в точке A, т.е это угол PointB-PointA-PointC

static inline double getCosAngle(CGPoint pointA,CGPoint pointB, CGPoint pointC){
    CGPoint vectorAB = CGPointMake(pointB.x-pointA.x, pointB.y-pointA.y);
    CGPoint vectorAC = CGPointMake(pointC.x-pointA.x, pointC.y-pointA.y);
    
    double dAB=sqrt(vectorAB.x*vectorAB.x+vectorAB.y*vectorAB.y);
    double dAC=sqrt(vectorAC.x*vectorAC.x+vectorAC.y*vectorAC.y);
    
    double scal = vectorAB.x * vectorAC.x +vectorAB.y * vectorAC.y;
    double angle = scal/(dAB*dAC);
    return angle;
}
//определение угла между тремя точками. Определяемый угол в точке A, т.е это угол PointB-PointA-PointC
static inline double getAngle(CGPoint pointA,CGPoint pointB, CGPoint pointC){
    if ((CGPointEqualToPoint(pointA, pointB))||(CGPointEqualToPoint(pointA, pointC))||(CGPointEqualToPoint(pointB, pointC))) 
        return 0; //если задано по сути не 3, а 2 точки то возвращаем ноль
    return  acos(getCosAngle(pointA, pointB, pointC));
}
#pragma -


Реализация:



//
//  UIOrientedPinchGestureRecognizer.m
//

#import "UIOrientedPinchGestureRecognizer.h"

@implementation UIOrientedPinchGestureRecognizer
@synthesize angle=_angle, orientation=_orientation,oScale=_lastScale;

#pragma mark init/dealloc
-(void) dealloc{
    [super dealloc];
}

-(id) init{
    self = [super init];
    if (self) {
        _orientation = OrientationNone;
        _angle       = 0;
    }
    return self;
}

#pragma mark - Methods 
-(void) detectAngle:(NSArray *) touches{
    if ([touches count]!=2) {
        _orientation= OrientationNone;
        _angle = 0;
        //вслучае больше двух пальцев считаем
        //что все коэф. были увеличины
        _lastScale.x *= self.scale/_oldScale;
        _lastScale.y *= self.scale/_oldScale;
        
        _oldScale = self.scale;
        return;
    }
    //Получим две точки
    CGPoint firstPoint =[[touches objectAtIndex:0] locationInView:self.view];
    CGPoint secondPoint = [[touches objectAtIndex:1] locationInView:self.view];
    
    //определим ближайшую к OY
    if (secondPoint.x<firstPoint.x) {
        CGPoint tmp = secondPoint;
        secondPoint = firstPoint;
        firstPoint = tmp;
    }
    
    //Найдем угол
    _angle = getAngle(firstPoint,secondPoint,CGPointMake(firstPoint.x,firstPoint.y-100.0f));
    
    //по углу определим ориентацию
    if ((_angle> M_PI_4/2.0f)&&(_angle< M_PI_4+M_PI_4/2.0f)) {
        _orientation=   OrientationDiagonal;
        _lastScale.x *= self.scale/_oldScale;
        _lastScale.y *= self.scale/_oldScale;
    }else 
        if ((_angle> M_PI_4+M_PI_4/2.0f)&&(_angle<M_PI_2+M_PI_4/2.0f)) {
            _orientation = OrientationHorizontal;
            _lastScale.x *= self.scale/_oldScale;
        }else
            if ((_angle>M_PI_2+M_PI_4/2.0f)&&(_angle<M_PI_2+M_PI_4+M_PI_4/2.0f)){
                _orientation = OrientationInvertedDiagonal;
                _lastScale.x *= self.scale/_oldScale;
                _lastScale.y *= self.scale/_oldScale;
            }else{
                _orientation=OrientationVertical;
                _lastScale.y *= self.scale/_oldScale;
            }
    _oldScale=self.scale;
    return;

}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    _lastScale = CGPointMake(1.0f, 1.0f);
    _oldScale = 1.0f;
    
    [self detectAngle:[touches allObjects]];
    [super touchesBegan:touches withEvent:event];
}

-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    [self detectAngle:[touches allObjects]];
    [super touchesMoved:touches withEvent:event];
}

@end


Использовать так же, как и UIPinchGestureRecognizer



 UIOrientedPinchGestureRecognizer *pinchGesture = [[UIOrientedPinchGestureRecognizer alloc]
                                              initWithTarget:self action:@selector(handlePinchGesture:)];
    [pinchGesture setDelegate:self];
    pinchGesture.cancelsTouchesInView=NO;
    [self.view addGestureRecognizer:pinchGesture];


Обработка:



- (IBAction)handlePinchGesture:(UIOrientedPinchGestureRecognizer *)sender {
     NSLog(@"angle: %f  orientation: %i scale.x:%f scale.y:%f",radToDeg(sender.angle),sender.orientation,sender.oScale.x,sender.oScale.y);                      
}


Недостатки:


На данный момент нет определения угла для трех/четырех и т.д. пальцев.

Источники



Спасибо всем за внимание.
Tags:
Hubs:
Total votes 27: ↑22 and ↓5+17
Comments4

Articles