UIPageViewController reconnaisseurs de gestes

je travaille sur une application depuis un moment maintenant, mais je ne pouvais pas poser cette question en raison de la NDA.

j'ai un UIPageViewController avec mon Viewcontroller. Les contrôleurs de vue ont des boutons qui sont dépassés par les reconnaisseurs de gestes des PageViewControllers. Par exemple, j'ai un bouton sur le côté droit du viewcontroller et lorsque vous appuyez sur le bouton, le Pageeviewcontroller prend la relève et change la page.

Comment puis-je faire que le bouton recevoir la touche et annuler le reconnaisseur de geste dans le Pageeviewcontroller?

je pense que le Controller PageView fait de mon Controller ViewController une sous-vue de sa vue.

je sais que je pourrais désactiver tous les gestes, mais ce n'est pas l'effet que je recherche.

je préfèrent ne pas sous-classe de la PageViewController comme apple affirme que cette classe n'est pas destiné à être sous-classé.

54
demandé sur spraff 2011-10-17 06:14:11

14 réponses

vous pouvez outrepasser

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
 shouldReceiveTouch:(UITouch *)touch

pour mieux contrôler quand le contrôleur PageView devrait recevoir le toucher et non. Regard sur " Empêcher les reconnaisseurs de gestes d'analyser les Touches "dans Dev API reconnaisseurs de gestes

ma solution ressemble à ceci dans le RootViewController pour le UIPageViewController:

dans viewDidLoad:

//EDITED Need to take care of all gestureRecogizers. Got a bug when only setting the delegate for Tap
for (UIGestureRecognizer *gR in self.view.gestureRecognizers) {
    gR.delegate = self;
}

Le remplacer:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    //Touch gestures below top bar should not make the page turn.
    //EDITED Check for only Tap here instead.
    if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
        CGPoint touchPoint = [touch locationInView:self.view];
        if (touchPoint.y > 40) {
            return NO;
        }
        else if (touchPoint.x > 50 && touchPoint.x < 430) {//Let the buttons in the middle of the top bar receive the touch
            return NO;
        }
    }
    return YES;
}

et n'oubliez pas de définir le RootViewController comme UIGestureRecognizerDelegate.

(pour information, je suis seulement en mode paysage.)

EDIT - le code ci-dessus traduit en Swift 2:

dans viewDidLoad:

for gr in self.view.gestureRecognizers! {
    gr.delegate = self
}

faire hériter le contrôleur de vue de page UIGestureRecognizerDelegate puis Ajouter:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if let _ = gestureRecognizer as? UITapGestureRecognizer {
        let touchPoint = touch .locationInView(self.view)
        if (touchPoint.y > 40 ){
            return false
        }else{
            return true
        }
    }
    return true
}
50
répondu jramer 2017-04-26 12:27:39

Voici une autre solution, qui peut être ajoutée dans le modèle viewDidLoad juste après la partie self.view.gestureRecognizers = self.pageViewController.gestureRecognizers du modèle Xcode. Il évite de jouer avec les tripes des reconnaisseurs de gestes ou de traiter avec ses délégués. Il enlève juste le reconnecteur de geste de robinet des vues, laissant seulement le reconnaisseur de glissement.

self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;

// Find the tap gesture recognizer so we can remove it!
UIGestureRecognizer* tapRecognizer = nil;    
for (UIGestureRecognizer* recognizer in self.pageViewController.gestureRecognizers) {
    if ( [recognizer isKindOfClass:[UITapGestureRecognizer class]] ) {
        tapRecognizer = recognizer;
        break;
    }
}

if ( tapRecognizer ) {
    [self.view removeGestureRecognizer:tapRecognizer];
    [self.pageViewController.view removeGestureRecognizer:tapRecognizer];
}

maintenant, pour passer d'une page à l'autre, vous devez glisser. Les robinets ne fonctionnent maintenant que sur vos commandes en haut de la page view (qui est ce que je recherchais).

54
répondu Pat McG 2012-05-06 02:44:42

j'ai eu le même problème. L'échantillon et la documentation font ceci dans loadView ou viewDidLoad:

self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;

cela remplace les reconnaisseurs de gestes des vues UIViewControllers avec les reconnaisseurs de gestes de L'UIPageViewController. Maintenant, quand une touche se produit, ils sont d'abord envoyés à travers les reconnaisseurs de gestes pageeviewcontrollers - s'ils ne correspondent pas, ils sont envoyés aux sous-vues.

décrochez juste cette ligne, et tout est fonctionne comme prévu.

Phillip

4
répondu Phillip 2016-05-09 19:42:55

réglage des gestureRecognizers délégué à un viewController comme ci-dessous ne travaille plus sur ios6

for (UIGestureRecognizer *gR in self.view.gestureRecognizers) {
    gR.delegate = self;
}

Dans ios6, le réglage de votre pageViewController de gestureRecognizers déléguer à un viewController provoque un blocage

3
répondu noRema 2017-05-23 11:54:38

j'ai utilisé

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

pour permettre aux clics de passer à travers les boutons à l'intérieur D'un UIPageViewController

2
répondu Adam Johns 2014-12-02 17:44:15
reconnaissez les sous-classes que vous ne pouvez pas vérifier. Par conséquent, la déclaration if ( [recognizer isKindOfClass:[UITapGestureRecognizer class]] ) n'est jamais exécutée.

j'ai choisi de mettre cancelsTouchesInView sur chaque recognizer à false, ce qui permet à des sous-vues du UIPageViewController de recevoir des touches ainsi.

Dans viewDidLoad :

guard let recognizers = self.pageViewController.view.subviews[0].gestureRecognizers else {
    print("No gesture recognizers on scrollview.")
    return
}

for recognizer in recognizers {
    recognizer.cancelsTouchesInView = false
}
2
répondu Hunter Maximillion Monk 2016-04-12 03:22:09

si vous utilisez un bouton que vous avez sous-classé, vous pouvez outrepasser touchesBegan, touchesMoved, et touchesEnded, en invoquant votre propre tour de page programmatique comme approprié, mais sans appeler super et passer les touches de la chaîne de notification.

0
répondu isaac 2011-10-17 02:29:14

peut également utiliser cela (merci pour l'aide, avec dire sur délégué):

// add UIGestureRecognizerDelegate

NSPredicate *tp = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@", [UITapGestureRecognizer class]];
UITapGestureRecognizer *tgr = (UITapGestureRecognizer *)[self.pageViewController.view.gestureRecognizers filteredArrayUsingPredicate:tp][0];
tgr.delegate = self; // tap delegating

NSPredicate *pp = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@", [UIPanGestureRecognizer class]];
UIPanGestureRecognizer *pgr = (UIPanGestureRecognizer *)[self.pageViewController.view.gestureRecognizers filteredArrayUsingPredicate:pp][0];
pgr.delegate = self; // pan delegating

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch 
{
    CGPoint touchPoint = [touch locationInView:self.view];

    if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]) && touchPoint.y > 915 ) {
        return NO; // if y > 915 px in portrait mode
    }

    if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]) && touchPoint.y > 680 ) {
        return NO; // if y > 680 px in landscape mode
    }

    return YES;
}

travailler parfaitement pour moi:)

0
répondu iTux 2012-11-29 15:47:13

C'est la solution qui fonctionne le mieux pour moi, j'ai essayé JRAMER réponse était bien sauf que j'ai eu l'Erreur lors de la pagination au-delà des limites (page 1 ou page 23 pour moi)

PatMCG solution ne m'a pas donné assez de flexibilité car il a annulé tous les taprecognizers, je voulais toujours le robinet, mais pas dans mon étiquette

dans mon UILabel j'ai simplement surévalué comme suit, ce robinet annulé pour mon étiquette seulement

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
 if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
     return NO;
 } else {
     return YES;
 }
}
0
répondu Ryan Heitner 2013-07-07 08:00:08

je crée pageviewcontrollers régulièrement pendant que mon utilisateur saute, boucles, et glisse vers différentes vues de page. Dans la routine qui crée un nouveau Controller PageView, j'utilise une version légèrement plus simple de l'excellent code ci-dessus:

UIPageViewController *npVC = [[UIPageViewController alloc]
                       initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
                       navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
                       options: options];
...

// Find the pageView tap gesture recognizer so we can remove it!
for (UIGestureRecognizer* recognizer in npVC.gestureRecognizers) {
    if ( [recognizer isKindOfClass:[UITapGestureRecognizer class]] ) {
        UIGestureRecognizer* tapRecognizer = recognizer;
        [npVC.view removeGestureRecognizer:tapRecognizer];
       break;
    }
}

maintenant les robinets fonctionnent comme je le souhaite (avec les robinets à gauche et à droite sautant une page, et les boucles fonctionnent très bien.

0
répondu Bill Cheswick 2014-06-29 17:01:10

créez simplement une sous-vue (liée à une nouvelle vue de gesturesliboutlet) dans votre contrôleur RootViewController et assignez les gestes à cette nouvelle vue. Cette vue couvre la partie de l'écran que vous voulez activer le geste.

dans le changement de viewDidLoad:

self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;

à:

self.gesturesView.gestureRecognizers = self.pageViewController.gestureRecognizers;
0
répondu tdf 2014-12-24 10:04:59

dans mon cas, je voulais désactiver le tapotement sur L'UIPageControl et laisser le tapotement être reçu par un autre bouton sur l'écran. Balayez fonctionne toujours. J'ai essayé de nombreuses façons et je crois que c'était la solution de travail la plus simple:

for (UIPageControl *view in _pageController.view.subviews) {
    if ([view isKindOfClass:[UIPageControl class]]) {
        view.enabled = NO;
    }
}

c'est obtenir la vue UIPageControl à partir des sous-vues UIPageController et désactiver l'interaction utilisateur.

0
répondu Leto Baxevanaki 2017-08-16 06:51:10

j'ai fini ici tout en cherchant un moyen simple, fourre-tout pour répondre aux robinets sur mon contrôleur de vue enfant dans un UIPageViewController. Le cœur de ma solution (Swift 4, Xcode 9) a fini par être aussi simple que ceci, dans mon RootViewController.swift (même structure que le modèle" Page-Based App "de Xcode):

override func viewDidLoad() {
    super.viewDidLoad()

    ...

    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(pageTapped(sender:)))
    self.pageViewController?.view.subviews[0].addGestureRecognizer(tapGesture)
}

...

@objc func pageTapped(sender: UITapGestureRecognizer) {
    print("pageTapped")
}

(j'ai également fait usage de cette réponse pour me permettre de garder une trace de la page qui a été effectivement tapé, c.-à-d.. l'actuel.)

0
répondu brookinc 2017-12-05 00:19:00

Swift 3 extension pour enlever le robinet de reconnaissance:

    import UIKit

extension UIPageViewController {
    func removeTapRecognizer() {
        let gestureRecognizers = self.gestureRecognizers

        var tapGesture: UIGestureRecognizer?
        gestureRecognizers.forEach { recognizer in
            if recognizer.isKind(of: UITapGestureRecognizer.self) {
                tapGesture = recognizer
            }
        }
        if let tapGesture = tapGesture {
            self.view.removeGestureRecognizer(tapGesture)
        }
    }
}
-1
répondu Roman Barzyczak 2017-04-29 10:20:40