web-dev-qa-db-fra.com

Comment ignorer les événements tactiles et les transmettre aux objets UIControl d'une autre sous-vue?

J'ai un UIViewController personnalisé dont UIView occupe un coin de l'écran, mais la plupart d'entre eux sont transparents, à l'exception des parties de celui-ci qui comportent des boutons et d'autres éléments. En raison de la disposition des objets sur cette vue, le cadre de la vue peut recouvrir certains boutons situés en dessous. Je veux pouvoir ignorer toutes les retombées sur cette vue si elles ne touchent rien d'important, mais il me semble que je ne peux transmettre que des événements tactiles réels (retouches touche End/NextResponder) Si j'ai un bouton UIB ou quelque chose du genre qui n'utilise pas touchesEnded, comment puis-je transmettre l'événement tactile à cela? 

Je ne peux pas simplement déterminer manuellement le sélecteur de bouton à appeler, car ce ViewController personnalisé peut être utilisé sur de nombreux affichages. J'ai fondamentalement besoin d'un moyen d'appeler cela:

[self.nextResponder touchesEnded:touches withEvent:event]; 

sur les types UIControl également. 

59
Chris C

La meilleure façon de le faire est probablement de remplacer hitTest:withEvent: dans la vue que vous voulez ignorer les touches. En fonction de la complexité de votre hiérarchie de vues, il existe deux manières simples de procéder.

Si vous avez une référence à la vue située sous la vue à ignorer:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *hitView = [super hitTest:point withEvent:event];

    // If the hitView is THIS view, return the view that you want to receive the touch instead:
    if (hitView == self) {
        return otherView;
    }
    // Else return the hitView (as it could be one of this view's buttons):
    return hitView;
}

Si vous n'avez pas de référence à la vue:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *hitView = [super hitTest:point withEvent:event];

    // If the hitView is THIS view, return nil and allow hitTest:withEvent: to
    // continue traversing the hierarchy to find the underlying view.
    if (hitView == self) {
        return nil;
    }
    // Else return the hitView (as it could be one of this view's buttons):
    return hitView;
}

Je recommanderais la première approche comme étant la plus robuste (s’il est possible d’obtenir une référence à la vue sous-jacente).

129
Stuart

Réponse rapide:

Vous pouvez créer une sous-classe UIView, IgnoreTouchView. Dans un storyboard, définissez-le sur la ou les vues du VC que vous souhaitez faire passer au toucher

class IgnoreTouchView : UIView {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let hitView = super.hitTest(point, with: event)
        if hitView == self {
            return nil
        }
        return hitView
    }
}

Notez que vous devez définir UserInteractionEnabled sur true pour UIView en question!

10
Kevin

Je n'ai pas trouvé un bon moyen de transmettre des événements UITouch entre des objets. Ce qui fonctionne généralement mieux est d’appliquer hitTest et de renvoyer nil si le point n’est pas dans la vue que vous souhaitez gérer:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
7
EricS

Vous pouvez également désactiver l'interaction utilisateur pour que les événements tactiles soient ignorés et transmis au parent. 

À Swift:  

yourView.isUserInteractionEnabled = false
5
Melkon

C'est une vieille question, et elle vient en tête des recherches. J'ai donc pensé poster une autre solution possible qui conviendrait mieux à votre situation. Dans mon cas, je voulais faire glisser une étiquette sur une autre étiquette et je voulais obtenir l'étiquette ci-dessous dans le hitTest. La chose la plus simple à faire est de définir userInteractionEnabled sur NO ou false, puis de faire votre test hit, puis de le rétablir si vous devez le déplacer à nouveau. Voici un exemple de code dans Swift:

override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
    let touch = touches.first
    let location = touch?.locationInView(self.view)
    self.label1.userInteractionEnabled = false
    let view = self.view.hitTest(location!, withEvent: nil) as? UILabel
    print(view?.text)
    self.label1.userInteractionEnabled = true
}
3
smileBot

Mon problème était similaire: j'avais un UIControl avec des répondeurs tactiles complexes, mais il agissait de manière amusante lorsqu'il était intégré dans une vue de défilement ... 

Cela empêche le défilement parent (ou peut être modifié pour empêcher d'autres interactions) lorsque la sous-vue a un geste tactile actif.

Ma solution:

Créez un protocole de délégué pour didBeginInteraction et didEndInteraction dans la sous-vue.

Appelez delegate.didBeginInteraction de touchesBegan

Appelez delegate.didEndInteraction depuis touchesEnded et touchesCancelled

Dans le contrôleur parent, implémentez les protocoles didBegin et didEnd, définissez le délégué

Définissez ensuite scrollView.scrollEnabled = NO dans didBegin, scrollEnabled = YES dans didEnd.

J'espère que cela aidera quelqu'un avec un cas d'utilisation légèrement différent de celui de la question posée!

0
Lytic