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é.
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
}
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).
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
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;
}
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
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
}
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.
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:)
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;
}
}
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.
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;
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.
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.)
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)
}
}
}