UISwipeGestureRecognizer调用了两次

时间:2010-11-19 15:03:57

标签: objective-c ios4 uigesturerecognizer

我正在实施我正在实施的观点。

这是一个在CATiledLayer中显示pdf页面的视图。该平铺视图位于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 个答案:

答案 0 :(得分:7)

我遇到了完全相同的问题,我的猜测是执行此行时触发的第二个动作:

[myScrollView removeFromSuperview];

出于某种原因,UISwipeGestureRecognizer会在删除附加到的视图时触发。没有其他UIGestureRecognizer可以做到这一点。

我的解决方案是在调用removeFromSuperview之前禁用所有手势识别器:

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

这不太理想,但却成功了。

答案 1 :(得分:5)

@Pacu,

不要使用计时器!

您需要查看手势状态。 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 :(得分:2)

在iOS 4.x.x上有一个错误,如果你在回调中删除了OverSuperview,会导致回调被调用两次。

您可以在removeFromSuperview中将enabled属性设置为NO:

- (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...
    }
}

答案 3 :(得分:1)

在将手势传递给委托之前,请尝试检查手势的状态属性:

-(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];
    }
}

如果可行,请使用向左滑动执行相同操作。

答案 4 :(得分:1)

以下代码是一个简单的解决方法,我认为不太可能产生任何不必要的副作用。

- (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....

当我从使用通知接收我的手势直接接收它们时,我遇到了这个问题,但我没有进一步研究这个问题。州财产对我来说绝对没有帮助 - 记录显示第一次和第二次呼叫使识别器处于同一状态。

答案 5 :(得分:1)

我遇到了同样的问题,似乎当你试图删除视图时出现问题(Gesture Fires),所以,重写removeFromSuperview ...这个片段对我有用......

- (void)removeFromSuperview
{
    for(UIGestureRecognizer* gesture in [self gestureRecognizers])
        [self removeGestureRecognizer:gesture];
    [super removeFromSuperview];
}

答案 6 :(得分:1)

我有完全相同的问题,您也可以向UIView类发送removeGestureRecognizer:消息。

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

但是,当从超级视图中删除视图时,我仍然不知道为什么手势被“触发”。

干杯,

答案 7 :(得分:-1)

当手势被用户“完成”时,aBitObvious的答案是好的,但这是开始挖掘问题的好地方。这里的事情是手势本身被检测一次,但与之相关的动作被触发两次。我做了一个计时器来跟踪这些调用之间有多少毫秒来忽略对操作的虚假调用。