UISwipeGestureRecognizer вызывается дважды - PullRequest
3 голосов
/ 19 ноября 2010

У меня проблемы с представлением, которое я реализую.

Это представление, которое показывает страницу PDF в CATiledLayer.Это мозаичное представление находится внутри UISCrollView.

Я имел представление, управляющее собой в качестве примера яблока "ZoomingPDFView".Я сделал несколько модификаций, чтобы он распознавал жесты смахивания, когда прокрутка не включена, и советы в различных темах и вопросах на этом сайте.В то время жесты назывались один раз .Но так как мне нужно было отделить представление и делегировать сканирование для кэширования страниц и сделать универсальное представление, я создал контроллер представления для обработки жестов прокрутки, а методы загрузки страниц повышают производительность представления PDF.

Теперь, когда у меня есть вид с одной стороны и контроллер с другой, жесты смахивания обнаруживаются дважды, и я не могу даже понять, в чем проблема.

это вывод консоли

2010-11-19 11:45:08.370 ZoomingPDFViewerForIPad[20327:207] initWithFrame and page
2010-11-19 11:45:08.530 ZoomingPDFViewerForIPad[20327:207] drawPage
2010-11-19 11:45:08.531 ZoomingPDFViewerForIPad[20327:207] scale: 1.000000
2010-11-19 11:45:08.531 ZoomingPDFViewerForIPad[20327:207] pdf scale: 1.290062
2010-11-19 11:45:08.532 ZoomingPDFViewerForIPad[20327:207] pdf initial scale: 1.290062
2010-11-19 11:45:15.488 ZoomingPDFViewerForIPad[20327:207] left
2010-11-19 11:45:15.489 ZoomingPDFViewerForIPad[20327:207] left
2010-11-19 11:45:15.490 ZoomingPDFViewerForIPad[20327:207] initWithFrame and page
2010-11-19 11:45:15.538 ZoomingPDFViewerForIPad[20327:207] drawPage
2010-11-19 11:45:15.538 ZoomingPDFViewerForIPad[20327:207] scale: 1.000000
2010-11-19 11:45:15.539 ZoomingPDFViewerForIPad[20327:207] pdf scale: 1.290062
2010-11-19 11:45:15.539 ZoomingPDFViewerForIPad[20327:207] pdf initial scale: 1.290062
2010-11-19 11:45:15.540 ZoomingPDFViewerForIPad[20327:1a07] initWithFrame and page
2010-11-19 11:45:15.541 ZoomingPDFViewerForIPad[20327:5f07] initWithFrame and page
2010-11-19 11:45:15.593 ZoomingPDFViewerForIPad[20327:1a07] drawPage
2010-11-19 11:45:15.594 ZoomingPDFViewerForIPad[20327:1a07] scale: 1.000000
2010-11-19 11:45:15.594 ZoomingPDFViewerForIPad[20327:1a07] pdf scale: 1.290062
2010-11-19 11:45:15.595 ZoomingPDFViewerForIPad[20327:1a07] pdf initial scale: 1.290062
2010-11-19 11:45:15.695 ZoomingPDFViewerForIPad[20327:5f07] drawPage
2010-11-19 11:45:15.704 ZoomingPDFViewerForIPad[20327:5f07] scale: 1.000000
2010-11-19 11:45:15.707 ZoomingPDFViewerForIPad[20327:5f07] pdf scale: 1.290062
2010-11-19 11:45:15.713 ZoomingPDFViewerForIPad[20327:5f07] pdf initial scale: 1.290062

вот код:

#import <UIKit/UIKit.h>

@class TiledPDFView;
@protocol PDFScrollViewDelegate;

@interface PDFScrollView : UIScrollView <UIScrollViewDelegate> {
 // The TiledPDFView that is currently front most
 TiledPDFView *pdfView;
 // The old TiledPDFView that we draw on top of when the zooming stops
 TiledPDFView *oldPDFView;


 // A low res image of the PDF page that is displayed until the TiledPDFView
 // renders its content.
 UIImageView *backgroundImageView;


 id<PDFScrollViewDelegate,NSObject> pdfViewDelegate;

 // current pdf zoom scale
 CGFloat pdfScale;

 CGPDFPageRef page;
 CGPDFDocumentRef pdf;
 CGFloat initialScale;
 TiledPDFView *initialTiledView;
 int currentPage;
 int pageCount;

 UITapGestureRecognizer *doubleTap,*twoFingerDoubleTap;
 UISwipeGestureRecognizer *rightSwipe, *leftSwipe;



}

@property (nonatomic,retain) id<PDFScrollViewDelegate> pdfViewDelegate;
-(id)initWithFrame:(CGRect)rect;
-(id)initWithFrame:(CGRect)frame andPDFPage:(CGPDFPageRef)aPage;
-(void)enableGestures;
-(void)drawPage;
@end

@implementation PDFScrollView
@synthesize pdfViewDelegate;

….

-(void)enableGestures{
 leftSwipe = [[UISwipeGestureRecognizer alloc ]initWithTarget:self action:@selector(handleRightSwipe:)];

 leftSwipe.direction = UISwipeGestureRecognizerDirectionRight;

 [self addGestureRecognizer:leftSwipe];

 //add right swipe
 rightSwipe = [[UISwipeGestureRecognizer alloc ]initWithTarget:self action:@selector(handleLeftSwipe:)];
 rightSwipe.direction = UISwipeGestureRecognizerDirectionLeft;
 [self addGestureRecognizer:rightSwipe];



 doubleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleDoubleTap:)];
 doubleTap.numberOfTapsRequired =2;
 doubleTap.numberOfTouchesRequired =1;
 [self addGestureRecognizer:doubleTap];


 twoFingerDoubleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleTwoFingerDoubleTap:)];
 twoFingerDoubleTap.numberOfTapsRequired =2;
 twoFingerDoubleTap.numberOfTouchesRequired =2;
 [self addGestureRecognizer:twoFingerDoubleTap];


}

// some more code
@end

#import <UIKit/UIKit.h>
#import "PDFScrollViewDelegate.h"
@class TiledPDFView;


@interface ZoomingPDFViewerForIPadViewController : UIViewController <UIScrollViewDelegate,PDFScrollViewDelegate>  {

 CGPDFPageRef page;



 CGPDFDocumentRef pdf;

 NSInteger currentPage;



 NSInteger pageCount;

 PDFScrollView *myScrollView;

 PDFScrollView *previousPage;
 PDFScrollView *nextPage;

}

-(id)initWithResourcePath:(NSString*)path ;
-(void)loadNextPage;
-(void)loadPreviousPage;
@end


@implementation ZoomingPDFViewerForIPadViewController

// some more code
#pragma mark -
#pragma mark PDFScrollViewDelegate methods
/* 
 called when user swipes right on the view
 */

-(void)viewDetectedRightSwipe:(PDFScrollView*)pdfScrollView withGesture:(UISwipeGestureRecognizer*)recognizer {
  NSLog(@"right");
 if (currentPage>1){
  //decreate page counter
  currentPage--;


  // release old next page

  if(nextPage){
   [nextPage release];
  }
  // set the actual page as the next one
  nextPage = [myScrollView retain];

  // remove the view from the actual view
  [myScrollView removeFromSuperview];

  // check if the previous page is loaded
  if(!previousPage)
   [self loadPreviousPage];

  // set the previouse page as the actual page
  myScrollView = previousPage;

  myScrollView.pdfViewDelegate = self;
  //[myScrollView drawPage];
  // load a new previous page
  //[NSThread detachNewThreadSelector:@selector(loadNextPage) toTarget:self withObject:nil];
  //[self loadNextPage];


 }
}
/*
 called when user swipes left on the view
 */
-(void)viewDetectedLeftSwipe:(PDFScrollView*)pdfScrollView withGesture:(UISwipeGestureRecognizer*)recognizer{
 NSLog(@"left");
 // if the end of the document isn't reached 
 if (currentPage<pageCount){
  //increment current page
  currentPage++;
  // if a previous page has been loaded release it
  if (previousPage) {
   [previousPage release];
  }
  // assing the actual view to as a previous page  and retain it before it gets release by superview
  previousPage = [myScrollView retain];
  // remove the view from the super view 
  [myScrollView removeFromSuperview];

  // if a next page hasn't beeen loaded yet, load it on this thread
  if (!nextPage)
   [self loadNextPage];

  // assign the next page as the current page
  myScrollView = nextPage;

  // put the current page the delegate
  myScrollView.pdfViewDelegate = self;

  // add the current page to the super view
  [[self view] addSubview:myScrollView];

  // load a next page.
  [NSThread detachNewThreadSelector:@selector(loadNextPage) toTarget:self withObject:nil];
 //[self loadNextPage];


 }  

}

/*
 called when the user taps the screen
 */
-(void)viewDetectedTapping:(PDFScrollView*)pdfScrollView withGesture:(UITapGestureRecognizer*)recognizer {
 NSLog(@"tapped");
 [myScrollView setZoomScale:1.0f animated:YES];


}


-(void)loadNextPage {
 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 CGPDFPageRef aPage =  CGPDFDocumentGetPage(pdf, currentPage+1);
 nextPage = [[PDFScrollView alloc] initWithFrame:myScrollView.frame andPDFPage:aPage ];
 [pool release];
}



-(void)loadPreviousPage {

 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
 CGPDFPageRef aPage =  CGPDFDocumentGetPage(pdf, currentPage-1);
 previousPage = [[PDFScrollView alloc] initWithFrame:myScrollView.frame andPDFPage:aPage];
 [pool release];
}
@end

это код, который вызывает жесты.

-(void)handleRightSwipe:(UIGestureRecognizer*)gesture {


    if ([pdfViewDelegate respondsToSelector:@selector(viewDetectedRightSwipe:withGesture:)]) {
        UISwipeGestureRecognizer *swipe = (UISwipeGestureRecognizer*)gesture;
        [pdfViewDelegate viewDetectedRightSwipe:self withGesture:swipe];

    }




}
-(void)handleLeftSwipe :(UIGestureRecognizer*)gesture{


    if ([pdfViewDelegate respondsToSelector:@selector(viewDetectedLeftSwipe:withGesture:)]) {
        UISwipeGestureRecognizer *swipe= (UISwipeGestureRecognizer*)gesture;
        [pdfViewDelegate viewDetectedLeftSwipe:self withGesture:swipe];

    }

}

Заранее спасибо за ваше время

Ответы [ 8 ]

7 голосов
/ 04 декабря 2010

У меня была точно такая же проблема, и я предполагаю, что второе действие сработало при выполнении этой строки:

[myScrollView removeFromSuperview];

По какой-то причине UISwipeGestureRecognizer срабатывает при удалении представления, к которому он присоединен. Никакой другой UIGestureRecognizer, кажется, не делает это.

Моим решением было отключить все распознаватели жестов перед вызовом removeFromSuperview:

for (UIGestureRecognizer *g in myScrollView.gestureRecognizers) {
    g.enabled = NO;
    g.delegate = nil;
}
[myScrollView removeFromSuperview];

Это не идеально, но сработало.

5 голосов
/ 02 декабря 2010

@ пак,

Не используйте таймер!

Вы должны смотреть на состояние жеста. UIGestureRecognizer отправляет вам информацию о том, какую часть жеста вы получаете. Жесты - это не одно событие. Подумайте о жесте «щепотка» ... он не просто происходит один раз, он начинается, меняет положение и может быть отменен, потерпеть неудачу или закончиться. Каждый раз, когда происходит что-то из этого, ваш обратный вызов запускается.

Обычное игнорирование событий, происходящих близко друг к другу, будет работать, но, например, жест смахивания может длиться дольше, чем ваше временное окно.

switch (sender.state) {
    case UIGestureRecognizerStateBegan:
        self.dragging = [self objectToRotateOrPinch:sender];
        break;
    case UIGestureRecognizerStateEnded:
        self.dragging = nil;
        break;
    case UIGestureRecognizerStateCancelled:
        self.dragging = nil;
        break;
    case UIGestureRecognizerStateFailed:
        self.dragging = nil;
        break;
    case UIGestureRecognizerStateChanged:
        // rotate or pinch
        break;
    default:
        break;
}

Если все, что вас волнует, это когда происходит свайп, вы захотите реагировать только тогда, когда состояние == UIGestureRecognizerStateEnded.

2 голосов
/ 29 июня 2012

В iOS 4.xx есть ошибка, из-за которой обратный вызов вызывался дважды, если вы удаляете внутреннюю функцию обратного просмотра.

Вы можете установить для свойства «включено» значение «Нет» в removeFromSuperview:

- (void)removeFromSuperview
{
    for(UIGestureRecognizer* gestureRecognizer in self.gestureRecognizers) {
        gestureRecognizer.enabled = NO;
    }
    [super removeFromSuperview];
}

- (void)willMoveToSuperview:(UIView *)newSuperview
{
    for(UIGestureRecognizer* gestureRecognizer in self.gestureRecognizers) {
        gestureRecognizer.enabled = YES;
    }
    [super willMoveToSuperview:newSuperview];
}

Однако обратный вызов все равно сработает, даже если он отключен.Поэтому вы должны проверить включенное свойство в обратном вызове:

- (void)didSwipeRight:(UISwipeGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer.enabled) {
            //do something useful...
    }
}
1 голос
/ 30 декабря 2011

У меня была точно такая же проблема, и вы могли бы также отправить removeGestureRecognizer: сообщение классу UIView.

-(void)handleLeftSwipe:(UIGestureRecognizer *)gesture
{
    UIView *vw = [gesture view];
    [view removeGestureRecognizer:gesture];
    [view removeFromSuperview];
}

Однако я до сих пор не знаю, почему этот жест «срабатывает», когдапредставление удаляется из суперпредставления.

Приветствия,

1 голос
/ 04 сентября 2011

У меня была такая же проблема, похоже, проблема возникает, когда вы пытаетесь удалить представление (Gesture Fires), поэтому перепишите removeFromSuperview ... Этот фрагмент работает у меня ...

- (void)removeFromSuperview
{
    for(UIGestureRecognizer* gesture in [self gestureRecognizers])
        [self removeGestureRecognizer:gesture];
    [super removeFromSuperview];
}
1 голос
/ 11 января 2011

Следующий код представляет собой простой обходной путь, который, я думаю, вряд ли будет иметь какие-либо нежелательные побочные эффекты.

- (void) leftSwipe: (UISwipeGestureRecognizer *) recognizer;
{
    if (![recognizer isEnabled]) return;    
    [recognizer setEnabled:NO];
    [recognizer performSelector:@selector(setEnabled:) withObject: [NSNumber numberWithBool:YES] afterDelay:0.1];
       // your gesture handling code here....

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

1 голос
/ 20 ноября 2010

Попробуйте проверить свойство состояния жеста, прежде чем передать его делегату:

-(void)handleRightSwipe:(UIGestureRecognizer*)gesture {
    if (gesture.state != UIGestureRecognizerStateEnded)
        return; //gesture not finished yet

    if ([pdfViewDelegate respondsToSelector:@selector(viewDetectedRightSwipe:withGesture:)]) {
        UISwipeGestureRecognizer *swipe = (UISwipeGestureRecognizer*)gesture;
        [pdfViewDelegate viewDetectedRightSwipe:self withGesture:swipe];
    }
}

Если это сработает, сделайте то же самое с проведением влево.

0 голосов
/ 02 декабря 2010

ответ aBitObvious хорош, когда жест обнаружен, поскольку он «делается» пользователем, но это действительно хорошее место, чтобы начать копаться в проблеме. Дело в том, что сам жест обнаруживается один раз, но действие, связанное с ним, запускается дважды. Я сделал таймер, чтобы отследить, сколько миллисекунд прошло между этими вызовами, чтобы игнорировать ложные вызовы к действию.

...