uiswipegesturerecognizerは2回呼び出しました
-
26-09-2019 - |
質問
実装しているビューに問題があります。
これは、カティレドレイヤーにPDFページを表示するビューです。そのタイル張りのビューは、uiscrollview内にあります。
「ZoomingPDFView」Appleの例として、自分自身を制御するビューを持っていました。スクロールが有効になっていないときにスワイプジェスチャーを認識し、このサイトのさまざまなスレッドや質問でアドバイスをするため、いくつかの変更を加えました。その時点でジェスチャーが呼ばれていました 一度. 。しかし、ビューを分離してスワイプを委任してページをキャッシュして多用途ビューを実行する必要があるため、スワイプジェスチャーを処理するビューコントローラーを作成し、ページの読み込み方法によりPDFビューのパフォーマンスが向上しました。
一方の側面と反対側にコントローラーが見えるようになったので、スワイプジェスチャーが2回検出され、問題の手がかりさえ得られません。
これはコンソール出力です
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];
}
}
よろしくお願いします
解決
iOS 4.xxにはバグがあり、コールバック内でFromsuperviewを削除すると、コールバックが2回呼び出されました。
removefromsuperviewで有効なプロパティを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...
}
}
他のヒント
私はまったく同じ問題を抱えていましたが、私の推測では、この行が実行されたときに2番目のアクションが発生したと思います。
[myScrollView removeFromSuperview];
何らかの理由で、uiswipegesturerecognizerは、それが取り付けられているビューが削除されたときに発火します。他のuigesturerecognizerはこれを行うようには見えません。
私の解決策は、removerfromsuperviewを呼び出す前に、すべてのジェスチャー認識者を無効にしました。
for (UIGestureRecognizer *g in myScrollView.gestureRecognizers) {
g.enabled = NO;
g.delegate = nil;
}
[myScrollView removeFromSuperview];
理想的ではありませんが、トリックをしました。
@pacu、
タイマーを使用しないでください!
ジェスチャー状態を見る必要があります。 uigesturerecognizerは、あなたが得ているジェスチャーのどの部分に関する情報を送信します。ジェスチャーは単一のイベントではありません。ピンチのジェスチャーについて考えてください...それは一度だけではなく、起動し、位置を変更し、キャンセルまたは失敗します。それらの1つが発生するたびに、コールバックが実行されます。
近くで起こるイベントを無視するだけでは通常は機能しますが、たとえば、スワイプジェスチャーは時間ウィンドウよりも長く続く可能性があります。
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;
}
あなたが気にするのは、スワイプが発生したときだけである場合、状態== uigesturerecognizerStatededの場合にのみ反応することをお勧めします。
ジェスチャーの財産をチェックしてから、それを代表者に渡す前に、
-(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];
}
}
それが機能する場合は、左スワイプで同じことをします。
次のコードは、不要な副作用がありそうにないという単純な回避策です。
- (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....
通知を使用してジェスチャーを受け取ることから直接受信することに切り替えたとき、私はこの問題に出くわしましたが、問題をさらに検討していません。州の財産は間違いなく私には役に立たなかった - ロギングは、同じ州の認識者が提供された最初と2番目の電話を示した。
私は同じ問題を抱えていましたが、ビューを削除しようとすると問題が発生しているようです(ジェスチャー火災)。
- (void)removeFromSuperview
{
for(UIGestureRecognizer* gesture in [self gestureRecognizers])
[self removeGestureRecognizer:gesture];
[super removeFromSuperview];
}
私はまったく同じ問題を抱えていました、そしてあなたも送ることができます removeGestureRecognizer:
uiviewの種類のクラスへのメッセージ。
-(void)handleLeftSwipe:(UIGestureRecognizer *)gesture
{
UIView *vw = [gesture view];
[view removeGestureRecognizer:gesture];
[view removeFromSuperview];
}
ただし、ビューがスーパーユーザーから削除されているときに、なぜジェスチャーが「トリガー」されているのかはまだわかりません。
乾杯、
Abitobviousの答えは、ユーザーが「完了」しているため、ジェスチャーが検出された場合に良いですが、問題を掘り始めるのに本当に良い場所です。ここでのことは、ジェスチャー自体が一度検出されることですが、それに関連するアクションが2回発射されます。アクションへの偽の呼び出しを無視するために、それらの呼び出しの間に何ミリ秒があるかを追跡するためにタイマーを行いました。