57 votes

Transférer l'UIGesture aux vues derrière

Je travaille sur une application iphone (iOS 4.0 ou plus) et j'ai quelques problèmes avec la gestion du toucher entre plusieurs vues. J'ai une structure de vue comme celle-ci

---> A superView 
     |
     ---> SubView - A 
     |
     ---> SubView - B (exactly on top of A, completely blocking A).

enter image description here

En fait, j'ai une super-vue, et des sous-vues A et B. B a le même cadre que A, ce qui fait que A est complètement caché.

Maintenant, mon exigence est la suivante.

  1. La sous-vue B doit recevoir tous les gestes de balayage et de tapotement (simples et doubles). gestes.
  2. La sous-vue A doit recevoir tous les gestes de pincement.

Voici comment j'ai ajouté les reconnaissances de gestes aux vues

UISwipeGestureRecognizer *leftSwipe  =  [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)];
[leftSwipe setDirection:(UISwipeGestureRecognizerDirectionLeft)];
leftSwipe.delegate  =   self;
[bView addGestureRecognizer:leftSwipe];
[leftSwipe release];

UISwipeGestureRecognizer *rightSwipe  =  [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(rightSwipe:)];
[rightSwipe setDirection:(UISwipeGestureRecognizerDirectionRight)];
rightSwipe.delegate   =   self;
[bView addGestureRecognizer:rightSwipe];
[rightSwipe release];

UIPinchGestureRecognizer *pinch   =  [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinch:)];
pinch.delegate    =   self;
[aView addGestureRecognizer:pinch];
[pinch release];

J'ai fait quelques recherches et pour moi UIGestureRecognizerDelegate semblait prometteur et j'ai implémenté la méthode du délégué

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer

J'ai renvoyé NO pour la sous-vue B, en espérant que la vue sous-jacente recevra ces événements. Mais pas de chance

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
    if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]] && [gestureRecognizer view] == bView) {
        NSLog(@"pinchGesture");
        //bView.userInteractionEnabled = NO;
        return NO;
    }
    return YES;
}

Ensuite, j'ai désactivé l'interaction de l'utilisateur avec la sous-vue B dans le rappel du délégué (code commenté dans le bloc ci-dessus), lorsque le geste du pincement est reconnu, en espérant que la partie restante du geste sera reçue par la sous-vue A. Pas de chance là non plus

Voilà où j'en suis maintenant. J'ai vu ce question, où la réponse acceptée implique la propriété cancelsTouchesInView . Mais je crois cancelsTouchesInView n'annulez qu'un événement tactile particulier, ne le faites pas suivre.

Y a-t-il un autre moyen de répondre à mes besoins ? Je suis prêt à travailler sur n'importe quel indice que vous fournissez.

EDIT : BOUNTY TIME

Ma soi-disant sous-vue A est en fait une instance de la classe View d'une bibliothèque tierce, qui supprime toutes les touches et je n'ai aucun contrôle sur les gestes. Je veux une implémentation différente pour les balayages gauche et droit et je veux que le pincement, le tapotement, etc. fonctionnent exactement comme avec cette vue tierce. J'ai donc placé une vue au-dessus de A (sous-vue B) pour obtenir les balayages gauche et droit. Mais maintenant, je veux transmettre d'autres événements de gestes à la bibliothèque sous-jacente.

18voto

SentineL Points 2157

Si j'ai bien compris votre problème, vous pouvez simplement ajouter une autre vue claire avec rectangle, la même que votre vue A et B, et implémenter tous les gestes sur celle-ci : lorsque vous faites un geste de pincement, vous contrôlez la sous-vue A, lorsque vous faites des gestes de balayage et de tapotement (simple et double) - vous contrôlez la sous-vue B. Vous pouvez le faire de différentes manières : via des pointeurs ou simplement en envoyant le geste reçu à la méthode dans la classe, qui contrôle votre sous-vue.

par exemple :

UISwipeGestureRecognizer *leftSwipe  =  [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)];
[leftSwipe setDirection:(UISwipeGestureRecognizerDirectionLeft)];
leftSwipe.delegate  =   subViewAcontroller;
[clearView addGestureRecognizer:leftSwipe];
[leftSwipe release];

UISwipeGestureRecognizer *rightSwipe  =  [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(rightSwipe:)];
[rightSwipe setDirection:(UISwipeGestureRecognizerDirectionRight)];
rightSwipe.delegate   =   subViewAcontroller;
[clearView addGestureRecognizer:rightSwipe];
[rightSwipe release];

UIPinchGestureRecognizer *pinch   =  [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinch:)];
pinch.delegate    =   subViewBcontroller;
[clearView addGestureRecognizer:pinch];
[pinch release];

ou :

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
    if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]]) {
        NSLog(@"pinchGesture");
        [subViewBcontroller solvePinchGesture: gestureRecognizer];
    }
//etc
    return YES;
}

15voto

flypig Points 611

Le geste de pincement ne sera jamais reçu par l'aView, car la hiérarchie de votre vue est incorrecte.

---> A superView 
 |
 ---> SubView - A 
 |
 ---> SubView - B (exactly on top of A, completely blocking A).

bView capturera tous les touchers multiples, y compris le geste de pincement, mais bView ne gérera pas le geste de pincement, il le transmettra donc au répondeur suivant dans la chaîne des répondeurs, c'est-à-dire le superView de bView ou le viewController de bView. L'événement de pincement ne sera jamais transmis à ses vues sœurs, car elles sont en parallèle (pas de relation parent-enfant ou vue-contrôleur).

Vous pouvez modifier la hiérarchie des vues comme suit :

---> A superView 
 |
 ---> SubView - A 
       |
       ---> SubView - B (child view of b, exactly on top of A, completely blocking A)

- (void)viewDidLoad {
    [super viewDidLoad];

    aView = [[UIView alloc] initWithFrame:self.view.bounds];
    aView.backgroundColor = [UIColor blueColor];

    bView = [[UIView alloc] initWithFrame:self.view.bounds];
    bView.backgroundColor = [UIColor redColor];

    [self.view addSubview:aView];
    [aView addSubview:bView];

    UISwipeGestureRecognizer *leftSwipe  =  [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)];
    [leftSwipe setDirection:(UISwipeGestureRecognizerDirectionLeft)];
    leftSwipe.delegate  =   self;
    [bView addGestureRecognizer:leftSwipe];

    UISwipeGestureRecognizer *rightSwipe  =  [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(rightSwipe:)];
    [rightSwipe setDirection:(UISwipeGestureRecognizerDirectionRight)];
    rightSwipe.delegate   =   self;
    [bView addGestureRecognizer:rightSwipe];

    UIPinchGestureRecognizer *pinch   =  [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinch:)];
    pinch.delegate    =   self;
    [aView addGestureRecognizer:pinch];
}

Veuillez obtenir plus d'informations en cliquant sur le lien ci-dessous : https://developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/EventsiPhoneOS/EventsiPhoneOS.html#//apple_ref/doc/uid/TP40009541-CH2-SW1

--> Note importante sur le passage des événements dans la chaîne du répondeur "Lorsque le système délivre un événement tactile, il l'envoie d'abord à une vue spécifique. Pour les événements tactiles, cette vue est celle renvoyée par hitTest:withEvent: ; pour les événements de mouvement de "secousse", les événements de télécommande, les messages d'action et les messages de menu d'édition, cette vue est le premier répondeur. Si la vue initiale ne traite pas l'événement, celui-ci remonte la chaîne des répondeurs en suivant un chemin particulier :

  1. La vue hit-test ou le premier répondant transmet l'événement ou le message à son contrôleur de vue s'il en a un ; si la vue n'a pas de contrôleur de vue, elle transmet l'événement ou le message à sa vue supérieure.
  2. Si une vue ou son contrôleur de vue ne peut pas gérer l'événement ou le message, il le transmet à la vue supérieure de la vue.
  3. Chaque vue supérieure suivante dans la hiérarchie suit le modèle décrit dans les deux premières étapes si elle ne peut pas traiter l'événement ou le message.
  4. La vue la plus élevée dans la hiérarchie des vues, si elle ne traite pas l'événement ou le message, le transmet à l'objet fenêtre pour traitement.
  5. L'objet UIWindow, s'il ne traite pas l'événement ou le message, le transmet à l'objet application singleton."

Ma réponse ci-dessus n'est pas la seule solution, ce que vous devez faire, c'est de faire en sorte que les logiques de gestion des gestes de balayage et de pincement soient dans la même chaîne de répondeurs, de sorte que l'événement de déblocage du geste de pincement soit transmis au bon répondeur. Par exemple, vous pouvez omettre la sous-vue b, et ajouter les gestes de balayage gauche/droite à la super-vue de aView.

1voto

Jonathan Cichon Points 3525

J'ai fait quelque chose de similaire il y a quelque temps. Je voulais faire deux choses différentes avec un doigt et deux ou plusieurs doigts. Ma solution pourrait nécessiter quelques ajustements pour vous et je ne peux pas garantir que tout fonctionnera exactement de la même manière dans votre environnement de code. Je n'ai pas trouvé de documentation appropriée sur hitTest et pourquoi il est appelé plusieurs fois de suite. J'ai donc fait quelques tests et j'ai trouvé une solution, comment séparer les gestes à un et à plusieurs doigts, qui a très bien fonctionné pour mes besoins.

J'avais cette hiérarchie de vues :

---> A superView 
 |
 ---> ScrollView - A - all touches
 |
 ---> ScrollView - B - only two+ finger scroll (exactly on top of A, completely blocking A).

J'ai implémenté le commutateur dans le hitTest du superView :

BOOL endDrag; // indicates scrollviewB did finish
BOOL shouldClearHitTest; // bool to help calling the method clearHitTest only once.
BOOL multiTouch; // needed for the method clearHitTest to know if a multi-touch is detected or not
NSMutableArray *events; // store all events within one eventloop/touch;

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    if (!endDrag) {
        if (!shouldClearHitTest) { // as hitTest will be called multible times in one event-loop and i need all events, i will clean the temporaries when everything is done
            shouldClearHitTest = YES;
            [[NSRunLoop mainRunLoop] performSelector:@selector(clearHitTest) target:self argument:nil order:1 modes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
        }
        [events addObject:event]; // store the events so i can access them after all hittests for one touch are evaluated

        if (event.type == UIEventTypeTouches && ([_events count] > 3 || [[event allTouches] count] > 0 || _currentEvent)) { // two or more fingers detected. at least for my view hierarchy
            multiTouch = YES;
            return scrollViewB;
        }
    }else {
        endDrag = NO;
    }
    return scrollViewA;
}

- (void)clearHitTest {
    if (shouldClearHitTest) {
        shouldClearHitTest = NO;
        [events removeAllObjects];
        if (multiTouch) {
           // Do some special stuff for multiTouch
        }
        multiTouch = NO;
    }
}

0voto

Kuldeep Points 1888

Je vous suggère d'utiliser la ligne de code ci-dessous, si nécessaire, pour vous aider.

//--->for bview gesture to detect swipe
[pinch requireGestureRecognizerToFail:leftSwipe]; && 
[pinch requireGestureRecognizerToFail:rightSwipe];

//--->for aview gesture to detect pinch
[rightSwipe requireGestureRecognizerToFail:rightSwipe]; ||
[leftSwipe requireGestureRecognizerToFail:pinch];

0voto

Warif Akhand Rishi Points 3407

Si bView a un geste de pincement. De l'action de geste de pincement de bView si appelé l'action de geste de pincement de aView alors...

UIPinchGestureRecognizer *pinchA   =  [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinchA:)];
[aView addGestureRecognizer:pinchA];
[pinchA release];    

UIPinchGestureRecognizer *pinchB   =  [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinchB:)];
[bView addGestureRecognizer:pinchB];
[pinchB release];

-(void)handlePinchA:(UIGestureRecognizer*)gestureRecognizer
{
NSLog(@"handlePinch on A");  
}

-(void)handlePinchB:(UIGestureRecognizer*)gestureRecognizer
{
NSLog(@"handlePinch on B");
[self handlePinchA:gestureRecognizer];  
}

aView est une librairie tierce. Pouvez-vous appeler handlePinchA de l'extérieur ? NSLog Reconnaisseurs de gestes m'imprime action=handlePinchA

NSLog(@"gestureRecognizers %@", [aView gestureRecognizers]);

Imprime-moi :

gestureRecognizers (
"<UIPinchGestureRecognizer: 0x8b3e500; state = Possible; view = <UIView 0x8b2f470>; target= <(action=handlePinchA:, target=....

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X