Есть ли способ отследить UIPinchGesture, когда один палец уходит, а затем возвращается? - PullRequest
1 голос
/ 20 января 2011

У меня есть приложение для iPad, которое широко использует UIGestureRecognizer всех ароматов. Вообще, я большой поклонник. У меня есть одна неприятная и очень специфическая проблема с UIPinchGestureRecognizer:

Рассмотрим сценарий, в котором пользователь делает жест щепоткой. Затем , не перемещая один палец ВСЕ , удаляет другой палец и заменяет его в другом месте и продолжает пинч. (Это не так невозможно, как кажется).

Моя проблема, похоже, заключается именно в общей неспособности UIGestureRecognizer замечать, когда палец покидает экран, и запускать какие-либо действия. Я заметил это и в других местах (touchSegan и touchesEnded), но я смог взломать его. Не в этот раз.

Вот как выглядит действие для UIGestureRecognizer:

  1. UIGestureRecognizerStateBegan, когда начинается пинч.
  2. n UIGestureRecognizerStateChangeds, а пинч меняется.
  3. Неловкое молчание , когда пользователь убирает палец.
  4. Неловкое молчание когда пользователь заменяет палец, вероятно, далеко.
  5. Другой UIGestureRecognizerStateChanged при возобновлении пинча. 5.

Теперь у меня возникла проблема на шаге 5. Поскольку в обработчике на шагах 3 и 4 ничего не происходит, он совершенно плавно переходит из UIGestureRecognizerStateChangeds в 2 и 5, но многое произошло. Для обработчика это выглядит так, как будто пользователь только что сделал невероятно быстрое повышение, когда мятежный палец перемещается между сотнями экранных сообщений за время между сообщениями обработчика.

Теперь, если бы пользователь не смог на самом деле так быстро ущипнуть, я мог бы просто установить порог для допустимых отклонений положения пальцев между обновлениями. Но это возможно. Быстрый жест щепотки действительно может заставить палец пользователя путешествовать на такие большие расстояния. Таким образом, кульминация проблемы заключается в следующем; Неспособность UIPinchGestureRecognizer отличить нечетную ситуацию, описанную выше, от быстрого жеста жестов. Мне нужно обращаться с этим совершенно по-разному, и сейчас у меня нет никакого способа сказать разницу. Почему ОС не может сказать мне, когда палец покидает экран? Это особенность емкостного экрана или ошибка ОС? Или ... намеренный дизайн ...?

1 Ответ

0 голосов
/ 21 января 2011

Я не думаю, что вы можете что-то сделать, чтобы UIPinchGestureRecognizer сообщал вам о касании пальцем вниз или вверх, но вы можете сделать свой собственный подкласс распознавателя жестов. Ниже представлен распознаватель жестов масштаба, похожий на пинч, и он различает жесты в одно касание и в два касания (в этом примере селектор handleOneTouchGesture пуст, но вы можете сделать что-то вроде вычисления расстояния перемещения).

//
//  RSScaleGestureRecognizer.h
//  Created by Jeff Argast
//

#import <Foundation/Foundation.h>

@interface RSScaleGestureRecognizer : UIGestureRecognizer {

}

@property (nonatomic, readonly) float   scale;

@end

И реализация:

//
//  RSScaleGestureRecognizer.m
//  Created by Jeff Argast
//

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

//
// Distance function
//

static CGFloat RSGetPointDistance (CGPoint p0, CGPoint p1);

//
// RSScaleGestureRecognizer private selectors
//

@interface RSScaleGestureRecognizer ()

- (void) handleTouchDown: (NSSet*) touches withEvent: (UIEvent*) event;
- (void) handleTouchMoved: (NSSet*) touches withEvent: (UIEvent*) event;
- (void) handleTouchUp: (NSSet*) touches withEvent: (UIEvent*) event;
- (void) handleOneTouchGesture: (NSSet*) allTouches;
- (void) handleTwoTouchGesture: (NSSet*) allTouches touchesMoved: (NSSet*) movedTouches;

@end

//
// UIView helper category
//

@interface UIView (RSScaleGestureRecognizer)

- (CGFloat) computeScaleFrom: (UITouch*) t0 to: (UITouch*) t1;

@end

//
// RSScaleGestureRecognizer Implementation
//

@implementation RSScaleGestureRecognizer

@synthesize scale;

- (id) initWithTarget:(id)target action:(SEL)action
{
    self = [super initWithTarget: target action: action];

    if ( self )
    {
        scale = 1.0f;
    }

    return self;
}

- (void)reset
{
    [super reset];

    scale = 1.0f;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{   
    [super touchesBegan: touches withEvent: event];

    [self handleTouchDown: touches withEvent: event];
}

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

    [self handleTouchMoved: touches withEvent: event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{   
    [super touchesEnded: touches withEvent: event]; 

    [self handleTouchUp: touches withEvent: event];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesCancelled: touches withEvent: event];

    [self handleTouchUp: touches withEvent: event];
}

- (void) handleTouchDown: (NSSet*) touches withEvent: (UIEvent*) event
{
    switch ( self. state )
    {
        case UIGestureRecognizerStateBegan:
        case UIGestureRecognizerStateChanged:
            break;

        case UIGestureRecognizerStatePossible:
            {
                NSSet* allTouches = [event touchesForGestureRecognizer: self];

                if ( allTouches.count > 2 )
                {
                    self.state = UIGestureRecognizerStateFailed;
                    return;
                }
            }
            break;

        default:
            self.state = UIGestureRecognizerStateFailed;
    }
}

- (void) handleTouchMoved: (NSSet*) movedTouches withEvent: (UIEvent*) event
{
    NSSet* allTouches = [event touchesForGestureRecognizer: self];

    switch ( allTouches.count )
    {
        case 1:
        {
            [self handleOneTouchGesture: allTouches];
        }
        break;

        case 2:
        {
            [self handleTwoTouchGesture: allTouches touchesMoved: movedTouches];
        }
        break;
    }
}

- (void) handleTouchUp: (NSSet*) touches withEvent: (UIEvent*) event
{   
    NSSet* allTouches = [event touchesForGestureRecognizer: self];

    int touchesRemaining = allTouches.count - touches.count;

    if ( touchesRemaining > 0 )
        return;

    switch ( self.state )
    {
        case UIGestureRecognizerStateBegan:
        case UIGestureRecognizerStateChanged:
            self.state = UIGestureRecognizerStateEnded;
            break;

        default:
            self.state = UIGestureRecognizerStateFailed;
    }

}

- (void) handleOneTouchGesture: (NSSet*) allTouches
{
    // Do something special here if desired when only one finger is touching
    return;
}

- (void) handleTwoTouchGesture: (NSSet*) allTouches touchesMoved: (NSSet*) movedTouches
{
    UIGestureRecognizerState currentState = self.state;

    switch ( currentState )
    {
        case UIGestureRecognizerStatePossible:  
        case UIGestureRecognizerStateBegan:
        case UIGestureRecognizerStateChanged:
            {
                UIView* selfView        = self.view;
                NSEnumerator* touchEnum = [allTouches objectEnumerator];
                UITouch* firstTouch     = [touchEnum nextObject];
                UITouch* secondTouch    = [touchEnum nextObject];

                scale = scale * [selfView computeScaleFrom: firstTouch to: secondTouch];        

                if ( currentState == UIGestureRecognizerStatePossible )
                {
                    self.state = UIGestureRecognizerStateBegan;
                }
                else 
                {
                    self.state = UIGestureRecognizerStateChanged;
                }
            }
            break;


        default:
            self.state = UIGestureRecognizerStateFailed;
    }
}

@end

//
// UIVIew category implementation
//

@implementation UIView (RSScaleGestureRecognizer)

- (CGFloat) computeScaleFrom: (UITouch*) t0 to: (UITouch*) t1
{
    UITouchPhase t0Phase = t0.phase;

    if ( (t0Phase == UITouchPhaseEnded) || (t0Phase == UITouchPhaseCancelled) || (t0Phase == UITouchPhaseBegan) )
        return 1.0;

    UITouchPhase t1Phase = t1.phase;

    if ( (t1Phase == UITouchPhaseEnded) || (t1Phase == UITouchPhaseCancelled) || (t1Phase == UITouchPhaseBegan) )
        return 1.0;

    CGPoint oldFirstPoint = [t0 previousLocationInView:self];
    CGPoint oldSecondPoint = [t1 previousLocationInView:self];
    CGFloat oldLength = RSGetPointDistance (oldFirstPoint, oldSecondPoint);

    CGPoint currentFirstPoint = [t0 locationInView:self];
    CGPoint currentSecondPoint = [t1 locationInView:self ];
    CGFloat currentLength = RSGetPointDistance (currentFirstPoint, currentSecondPoint);

    // Avoid divide by zero
    if ( oldLength < 0.01f )
        return 1.0f;

    return currentLength / oldLength;
}

@end

//
// Distance function implementation
//

CGFloat RSGetPointDistance (CGPoint p0, CGPoint p1)
{
    CGFloat xDiff = p0.x - p1.x;
    CGFloat yDiff = p0.y - p1.y;

    return sqrtf ((xDiff * xDiff) + (yDiff * yDiff));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...