UIPageViewController non restituisce alcun Gesture Recognizer in iOS 6

Sto cercando di disabilitare il riconoscimento di pan gesture per un UIPageViewController.

Su iOS 5 posso scorrerli e disabilitarli.

for (UIGestureRecognizer* recognizer in self.pageViewController.gestureRecognizers) { if ([recognizer isKindOfClass:[UIPanGestureRecognizer class]]) { recognizer.enabled = NO; } } 

Su iOS 6 utilizzando UIPageViewControllerTransitionStyleScroll non sono presenti riconoscitori di gesti restituiti da Page View Controller.

Una precisazione

Questo può essere ridotto a:

self.pageViewController.gestureRecognizers = 0 quando lo stile di transizione di UIPageViewController è impostato per lo scorrimento, quindi non posso accedere ai riconoscitori di gesti.

    C’è un modo per aggirare questo? Non penso di fare nulla di sbagliato dal momento che la transizione del ricciolo funziona bene.

    C’è un bug archiviato nel radar per questo comportamento. Quindi, scommetto che fino a quando Apple non lo risolverà non ci sarà alcuna possibilità di risolverlo.

    Una soluzione che mi viene in mente è quella di porre una sottoview trasparente su UIPageViewController e aggiungere ad essa un UIPanGestureRecognizer per intercettare quel tipo di gesto e non inoltrare ulteriormente. È ansible abilitare questa vista / riconoscitore quando è richiesta la distriggerszione del gesto.

    L’ho provato con una combinazione di riconoscimenti di gesture Pan e Tap e funziona.

    Questo è il mio codice di prova:

     - (void)viewDidLoad { [super viewDidLoad]; UIPanGestureRecognizer* g1 = [[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(g1Pan:)] autorelease]; [self.view addGestureRecognizer:g1]; UITapGestureRecognizer* s1 = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(g1Tap:)] autorelease]; [self.view addGestureRecognizer:s1]; UIView* anotherView = [[[UIView alloc]initWithFrame:self.view.bounds] autorelease]; [self.view addSubview:anotherView]; UIPanGestureRecognizer* g2 = [[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(g2Pan:)] autorelease]; [anotherView addGestureRecognizer:g2]; } 

    Quando g2 è abilitato, impedirà il riconoscimento di g1 . D’altra parte, non impedirà il riconoscimento di s1.

    Capisco che questo è un hack, ma di fronte a un bug apparente in UIPageViewController (almeno, il comportamento effettivo è palesemente diverso da quello che gli stati di riferimento), non riesco a vedere alcuna soluzione migliore.

    Trovato in UIPageViewController.h:

    // Compilato solo se lo stile di transizione è ‘UIPageViewControllerTransitionStylePageCurl’. @property (nonatomic, readonly) NSArray * gestureRecognizers;

    Quindi, non un bug: progettando la paginaViewController non si ottengono riconoscitori di gesti quando lo stile di scorrimento è impostato.

    Puoi sempre provare a disabilitare l’interazione dell’utente nella vista secondaria del controller della visualizzazione della pagina:

     for (UIScrollView *view in self.pageViewController.view.subviews) { if ([view isKindOfClass:[UIScrollView class]]) { view.scrollEnabled = NO; } } 

    In base al file di intestazione UIPageViewController, quando l’origine dati è nulla, la navigazione guidata da gesti è disabilitata.

    Quindi, impostare l’origine dati su zero quando si desidera disabilitare lo scorrimento, quindi quando si desidera abilitare lo scorrimento, ripristinare l’origine dati.

    vale a dire

     // turns off paging pageViewController.datasource = nil // turns on paging pageViewController.datasource = self; 

    È ansible accedere a UIPanGestureRecognizer tramite UIScrollview da UIPageViewController.

     for (UIView *view in self.pageController.view.subviews) { if ([view isKindOfClass:[UIScrollView class]]) { UIScrollView *scrollView = (UIScrollView *)view; UIPanGestureRecognizer* panGestureRecognizer = scrollView.panGestureRecognizer; [panGestureRecognizer addTarget:self action:@selector(move:)]; } } 

    Se UIPageViewControllers UIPanGestureRecognizer sta mangiando / inghiottendo tutti gli eventi da un altro PanGestureRecognizer (ad esempio un menu scorrevole).

    È ansible espandere facilmente la soluzione Bernies e rendere UIScrollViews PanGestureRecognizer necessario che l’altro Riconoscitore abbia esito negativo. Qualcosa come questo:

     for (UIView *view in pageViewController.view.subviews) { if ([view isKindOfClass:[UIScrollView class]]){ UIScrollView *scrollView = (UIScrollView *)view; [scrollView.panGestureRecognizer requireGestureRecognizerToFail:otherRecognizer]; } } 

    In questo modo, il PanGestureRecognizer a scorrimento si triggers solo nelle aree in cui l’ho voluto.

    Questa potrebbe non essere la soluzione migliore per il futuro, dal momento che richiediamo a Apple di non modificare l’uso interno di UIScrollView da parte di UIPageViewControllers. ma…

    Supponendo che tu possa trovare i riconoscitori di gesti e rimuoverli è molto fragile. Stai utilizzando la conoscenza presunta di come Apple utilizza UIPageViewController per fornire funzionalità nella tua app. Se questo cambia (come tra iOS 5 e iOS 6), allora l’app di codice inizierà a comportarsi in modi imprevisti. È quasi come utilizzare un’API privata: non è garantito che funzioni con la prossima versione del sistema operativo.

    Cosa succede se usi i metodi KVC (Key Value Coding) per accedere ai riconoscitori? (Non sono dove posso testare questo momento).

    Un test rapido potrebbe essere quello di recuperare il numero di gestureRecognizers:

     [self.pageViewController countOfKey:@"gestureRecognizers"]; 

    Se funziona, puoi andare oltre per recuperare l’array di riconoscitori:

     NSArray *recognizers = [self.pageViewController mutableArrayValueForKey:@"gestureRecognizers"]; 

    Sottile, ma forse …

    Modifica: è stato finalmente in grado di testare. Usato il seguente:

     NSArray *pvcGRsNoKVC = [[NSArray alloc] initWithArray:self.pageViewController.gestureRecognizers]; NSArray *viewGRsNoKVC = [[NSArray alloc] initWithArray:self.view.gestureRecognizers]; NSArray *pvcGRsKVC = [[NSArray alloc] initWithArray:[self.pageViewController valueForKey:@"gestureRecognizers"]]; NSArray *viewGRsKVC = [[NSArray alloc] initWithArray:[self.view valueForKey:@"gestureRecognizers"]]; 

    Non ha fatto alcuna differenza. Lo stile del ricciolo ha funzionato bene in entrambe le direzioni; lo stile di scorrimento mostrava gli array come non nulli, ma vuoti. Una cosa interessante, però, è stata che la vista ANCHE non ha rinunciato ai suoi riconoscitori – anche se la funzionalità di scorrimento è lì, quindi deve avere almeno un riconoscimento pan …

    Un’altra soluzione, solo per la storia. Puoi rendere qualsiasi vista come un interruttore di riconoscimento dei gesti e dovrebbe funzionare nel rettangolo di quella vista. Deve esserci un altro UIPanGestureRecognizer con delegato. Può essere qualsiasi object con un metodo:

     static UIPageViewController* getPageViewControllerFromView(UIView* v) { UIResponder* r = v.nextResponder; while (r) { if ([r isKindOfClass:UIPageViewController.class]) return (UIPageViewController*)r; r = r.nextResponder; } return nil; } - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { if (getPageViewControllerFromView(otherGestureRecognizer.view)) { otherGestureRecognizer.enabled = NO; otherGestureRecognizer.enabled = YES; } return NO; } 

    È ansible utilizzare la seguente class per scopi di riconoscimento del riconoscimento:

     @interface GestureRecognizerBreaker : NSObject  { UIGestureRecognizer* breaker_; BOOL(^needsBreak_)(UIGestureRecognizer*); } - (id) initWithBreakerClass:(Class)recognizerClass checker:(BOOL(^)(UIGestureRecognizer* recognizer))needsBreak; - (void) lockForView:(UIView*)view; - (void) unlockForView:(UIView*)view; @end @implementation GestureRecognizerBreaker - (void) dummy:(id)r {} - (id) initWithBreakerClass:(Class)recognizerClass checker:(BOOL(^)(UIGestureRecognizer*))needsBreak { self = [super init]; if (!self) return nil; NSParameterAssert([recognizerClass isSubclassOfClass:UIGestureRecognizer.class] && needsBreak); needsBreak_ = needsBreak; breaker_ = [[recognizerClass alloc] initWithTarget:self action:@selector(dummy:)]; breaker_.delegate = self; return self; } - (BOOL) gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer { if (needsBreak_(otherGestureRecognizer)) { otherGestureRecognizer.enabled = NO; otherGestureRecognizer.enabled = YES; } return NO; } - (void) lockForView:(UIView*)view { [view addGestureRecognizer:breaker_]; } - (void) unlockForView:(UIView*)view { [view removeGestureRecognizer:breaker_]; } @end 

    Questo funziona, ad esempio come singletone:

     static GestureRecognizerBreaker* PageViewControllerLocker() { static GestureRecognizerBreaker* i = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ i = [[GestureRecognizerBreaker alloc] initWithBreakerClass:UIPanGestureRecognizer.class checker:^BOOL(UIGestureRecognizer* recognizer) { UIView* v = recognizer.view; UIResponder* r = v.nextResponder; while (r) { if ([r isKindOfClass:UIPageViewController.class]) return YES; r = r.nextResponder; } return NO; }]; }); return i; } 

    Dopo aver chiamato -lockForView: il gesto del controllore di pagina non funziona sul trascinamento nella cornice della vista. Ad esempio voglio bloccare tutto lo spazio del mio controller di visualizzazione. Quindi, ad un certo punto, il metodo di controllo che chiamo

     [PageViewControllerLocker() lockForView:self.view]; 

    E ad un altro punto

     [PageViewControllerLocker() unlockForView:self.view]; 

    All’interno di pageviewcontroller c’è una sottoview chiamata quieingscrollview che ha 3 riconoscimenti gestuali che controllano il controller della vista della pagina per farli usare

     [[pageViewController.view.subviews objectAtIndex:0] gestureRecognizers] 

    Nel mio caso, ho avuto un “pannello informativo” di UIView che è venuto giù sul mio UIPageViewController, ma i riconoscitori di gesti del controller di visualizzazione della pagina hanno interferito con la navigazione attraverso il mio pannello informativo.

    La mia soluzione era quella di impostare il dataSource su zero, ma anche di non consentire al controller della visualizzazione della pagina di aggiornare il focus mentre il pannello delle informazioni è attivo:

     - (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context { if (self.infoPanel) { return NO; } else { return YES; } } 

    È ansible utilizzare i metodi delegati di UIGestureRecognizer per UIGestureRecognizer e disabilitare qualsiasi gesto. Ad esempio, è ansible utilizzare questo callback delegato: gestureRecognizer:shouldReceiveTouch: Assicurati di impostare il delegato per tutti i riconoscitori.