2010-11-19 50 views
3

我遇到了我正在實現的視圖的問題。UISwipeGestureRecognizer調用兩次

這是一個顯示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]; 

    } 

} 

預先感謝您的時間

+0

請顯示handleRightSwipe和/或handleLeftSwipe的代碼。 – Anna 2010-11-19 15:09:31

+0

我剛剛做了一個編輯。謝謝(順便提一下,很棒的用戶名) – Pacu 2010-11-20 19:39:48

回答

1

在iOS上4.x.x有一個bug,導致回調被調用,如果你兩次回調裏面removeFromSuperview。

您可以設置enabled屬性爲NO在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

嘗試將它傳遞給委託之前檢查操作的狀態屬性:

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

有些時候有效。看起來手勢已結束,然後該方法被觸發兩次。這出現在我最後的谷歌搜索(具有相同的條款:S)http://stackoverflow.com/questions/4226239/uiswipegesturerecognizer-called-twice/4234640#4234640 – Pacu 2010-11-20 21:42:44

+0

嘗試把狀態檢查在手勢處理程序。還要確保手勢識別器只添加一次到視圖中。是否有可能多次調用enableGestures? – Anna 2010-11-20 22:31:56

-1

當手勢被用戶「完成」檢測到手勢時,aBitObvious的答案很好,但這是真正開始深入研究問題的好地方。這裏的事情是手勢本身被檢測到一次,但與它相關的動作被觸發兩次。我做了一個計時器來跟蹤這些呼叫之間的毫秒數,以忽略對該操作的虛假呼叫。

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。

+0

我會試試Kenny。謝謝!我不喜歡計時器 – Pacu 2010-12-02 00:40:50

+1

嘿肯尼,我已經試過了,似乎手勢識別器只是通知「結束」狀態,而不是「之間」狀態,如更改,失敗,取消。被解僱的是兩個「結束」狀態。說到「UILongpressGestureRecognizer」,你說的是真的,但似乎並非如此。 – Pacu 2010-12-08 19:28:35

7

我有完全相同的問題,我的猜測是,在執行這條線時,第二個動作射擊:

[myScrollView removeFromSuperview]; 

出於某種原因,UISwipeGestureRecognizer火災時,被刪除它附着的觀點。沒有其他UIGestureRecognizer出現這樣做。

我的解決辦法是在調用removeFromSuperview前,請關閉所有的手勢識別:

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

這是不理想,但它的伎倆。

+1

我一直在觀察這個傑夫,在我的情況下似乎可能,但控制檯輸出顯示,否則。無論如何,我已經嘗試了很多事情,以至於我也會爲您的解決方案提供一個解決方案。 – Pacu 2010-12-08 19:31:02

+1

我看到使用SDK 4.2的情況完全相同:如果視圖從超級視圖中移除,則再次刷新火焰!奇怪的。 – Krumelur 2011-03-10 16:12:36

+0

我也證實了這一點。看起來像一個錯誤。 – Kamchatka 2011-06-13 01:34:13

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

我碰到這個問題時,我使用的通知來接受我的手勢,直接接收他們交換,但我還沒有進一步調查過的問題。州財產對我來說絕對沒有幫助 - 記錄顯示第一次和第二次調用使識別器處於相同狀態。

1

我有同樣的問題,看來,當您嘗試刪除視圖(手勢火災)發生的問題,因此,改寫removeFromSuperview ...這sniplet對我的作品......

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

我有完全相同的問題,你也可以發送removeGestureRecognizer:消息到UIView類的類。

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

但是,我仍然不知道爲什麼從超級視圖中刪除視圖時手勢是'觸發'的。

乾杯,