2010-11-19 13 views
3

Ich habe Probleme mit einer Ansicht, die ich implementiere.UISwipeGestureRecognizer zweimal aufgerufen

Es ist eine Ansicht, die eine PDF-Seite in einem CATiledLayer zeigt. Diese gekachelte Ansicht befindet sich in einer UISCrollView.

Ich hatte die Ansicht selbst als "ZoomingPDFView" Apple-Beispiel steuern. Ich habe ein paar Änderungen vorgenommen, damit es Wischgesten erkennt, wenn das Scrollen nicht aktiviert ist, und Ratschläge in verschiedenen Threads und Fragen auf dieser Site. Zu dieser Zeit wurden die Gesten einmal genannt. Aber da ich die Ansicht entkoppeln und das Wischen delegieren musste, um die Seiten zwischenzuspeichern und eine vielseitige Ansicht zu erstellen, erstellte ich einen Ansichtscontroller, um die Wischgesten zu handhaben, und die Seitenlademethoden erhöhen die Leistung der PDF-Ansicht.

Jetzt, da ich die Ansicht auf der einen Seite und den Controller auf der anderen Seite habe, werden Wischgesten zweimal erkannt und ich kann nicht einmal eine Ahnung von dem Problem bekommen.

dies ist die Ausgabe der Konsole

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 

hier ist der Code:

#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 

dies ist der Code, der die Gesten auslösen.

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

    } 

} 

Vielen Dank im Voraus für Ihre Zeit

+0

zeigen Sie den Code für handleRightSwipe und/oder handleLeftSwipe. – Anna

+0

Ich habe gerade einen Schnitt gemacht. Danke (genialer Benutzername übrigens) – Pacu

Antwort

1

Auf iOS 4.x.x gibt es einen Fehler, die der Rückruf verursacht zweimal aufgerufen werden, wenn Sie innerhalb Rückruf removeFromSuperview.

Sie können aktiviert Eigenschaft auf NO in 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]; 
} 

Allerdings wird der Rückruf noch Feuer, auch wenn sie deaktiviert ist. So sollten Sie dann überprüfen Eigenschaft enabled in Rückruf:

- (void)didSwipeRight:(UISwipeGestureRecognizer *)gestureRecognizer 
{ 
    if (gestureRecognizer.enabled) { 
      //do something useful... 
    } 
} 
1

Versuchen Sie, das Staatseigentum der Geste zu prüfen, bevor es an die Delegaten vorbei:

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

Wenn das funktioniert, das gleiche mit der Wischen links.

+0

das hat mal geklappt. Es scheint, dass die Geste beendet ist und die Methode zweimal ausgelöst wird. Dies tauchte in meiner letzten Google-Suche auf (mit den gleichen Begriffen: S) http://stackoverflow.com/questions/4226239/uiswipegesture-recognizer-called-twice/4234640#4234640 – Pacu

+0

Versuchen Sie, den Status-Check in die Gesten-Handler zu setzen. Stellen Sie außerdem sicher, dass die Gestenerkenner nur einmal zur Ansicht hinzugefügt werden. Ist es möglich, dass enableGestures mehr als einmal aufgerufen wird? – Anna

-1

Die Antwort von aBitObvious ist gut, wenn die Geste erkannt wird, da sie vom Benutzer "erledigt" wird, aber es ist wirklich ein guter Ort, um mit dem Problem zu beginnen. Die Geste besteht darin, dass die Geste selbst einmal erkannt wird, aber die damit verbundene Aktion zweimal ausgelöst wird. Ich habe einen Timer gemacht, um zu verfolgen, wie viele Millisekunden zwischen diesen Anrufen liegen, um falsche Aufrufe an die Aktion zu ignorieren.

5

@Pacu,

Verwenden Sie keinen Timer!

Sie müssen den Gestenstatus betrachten. Der UIGestureRecognizer sendet Ihnen Informationen darüber, welchen Teil der Geste Sie erhalten. Gesten sind kein einzelnes Ereignis. Denken Sie an eine Prise Geste ... es passiert nicht nur einmal, es beginnt, ändert die Position und kann abgebrochen werden oder fehlschlagen oder enden. Jedes Mal, wenn eines dieser Dinge passiert, wird dein Callback ausgeführt.

Das Ignorieren von Ereignissen, die nahe beieinander liegen, wird normalerweise funktionieren, aber zum Beispiel könnte eine Wischgeste länger als Ihr Zeitfenster dauern.

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

Wenn alles, was Sie über ist egal, wenn ein Swipe passiert ist, nur, wenn der Staat reagieren wollen werde == UIGestureRecognizerStateEnded.

+0

Ich werde das ausprobieren Kenny. Vielen Dank! Ich mag den Timer auch nicht – Pacu

+1

Hey Kenny, ich habe es ausprobiert, und es scheint, dass die Gestenerkenner nur den "beendeten" Zustand informiert, und nicht die "dazwischen" Zustände wie geändert, gescheitert, abgebrochen. Was gefeuert wird, sind zwei "beendete" Staaten. Was Sie sagen, es ist wahr, wenn es um "UILongpressGestureRecognizer" geht, aber das scheint nicht der Fall zu sein. – Pacu

7

hatte ich genau das gleiche Problem und meine Vermutung ist, dass die zweite Aktion ausgelöst, wenn diese Zeile ausgeführt wurde:

[myScrollView removeFromSuperview]; 

Aus irgendeinem Grund Brände UISwipeGestureRecognizer, wenn die Ansicht es angebracht ist entfernt wird. Kein anderer UIGestureRecognizer scheint dies zu tun.

Meine Lösung war, alle Gesten Erkenner vor dem Aufruf removeFromSuperview deaktiviere:

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

Es ist nicht ideal, aber es hat den Trick.

+1

Ich habe diesen Jeff beobachtet, es scheint in meinem Fall möglich, aber die Konsolenausgabe zeigt das Gegenteil. Wie auch immer, ich habe so viele Dinge ausprobiert, dass ich auch deinem Workaround eine Chance gebe. – Pacu

+1

Ich sehe genau dasselbe passiert mit SDK 4.2: Swipe feuert ein anderes Mal, wenn die Ansicht aus Superview entfernt wird! Seltsam. – Krumelur

+0

Ich bestätige das auch. Sieht nach einem Fehler aus. – Kamchatka

1

Der folgende Code ist eine einfache Problemumgehung, die wahrscheinlich keine unerwünschten Nebenwirkungen hat.

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

Ich kam in dieser Frage, als ich von der Nutzung der Benachrichtigungen geschaltet meine Gesten sie direkt auf dem Empfang zu empfangen, aber ich habe nicht weiter in die Sache untersucht. Die staatliche Eigenschaft war mir definitiv nicht behilflich - Logging zeigte erste und zweite Aufrufe, die den Erkenner im selben Zustand bereitstellten.

1

hatte ich das gleiche Problem scheint, dass das Problem auftritt, wenn Sie versuchen, die Ansicht (Gesture Fires) zu entfernen, so, removeFromSuperview umschreiben ... Diese Sniplet funktioniert für mich ...

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

I hatte genau das gleiche Problem und Sie könnten auch removeGestureRecognizer: Nachricht an die UIView Art der Klasse senden.

Allerdings weiß ich immer noch nicht, warum die Geste 'ausgelöst' wird, wenn die Ansicht aus der Superview entfernt wird.

Cheers,

Verwandte Themen