Bien que la plupart des documents Apple soient très bien écrits, je pense que « Guide de gestion des événements pour iOS » est une exception. Il m’est difficile de comprendre clairement ce qui a été décrit ici.
Le document dit:
Dans les tests de hit, une fenêtre appelle
hitTest:withEvent:
dans la vue la plus haute de la hiérarchie des vues; cette méthode procède en appelant récursivementpointInside:withEvent:
sur chaque vue de la hiérarchie de vues qui renvoie YES, en descendant la hiérarchie jusqu’à ce qu’elle trouve la sous-vue dans les limites de laquelle le contact a eu lieu. Cette vue devient la vue de test de hit.
Alors, est-ce que seul hitTest:withEvent:
de la vue la plus haute est appelé par le système, qui appelle pointInside:withEvent:
de toutes les sous-vues, et si le retour d’une sous-vue spécifique est OUI, appelle alors pointInside:withEvent:
des sous-classes de cette sous-vue?
Cela semble être une question fondamentale. Mais je suis d’accord avec vous, le document n’est pas aussi clair que d’autres documents, alors voici ma réponse.
L’implémentation de hitTest:withEvent:
dans UIResponder effectue les opérations suivantes:
pointInside:withEvent:
of self
hitTest:withEvent:
renvoie nil
. la fin de l’histoire. hitTest:withEvent:
à ses sous-vues. il commence à partir de la sous-vue de niveau supérieur et continue vers d’autres vues jusqu’à ce qu’une sous-vue retourne un object non nil
ou que toutes les sous-vues reçoivent le message. nil
la première fois, le premier hitTest:withEvent:
renvoie cet object. la fin de l’histoire. nil
, le premier hitTest:withEvent:
renvoie self
Ce processus se répète de manière récursive, de sorte que la vue feuille de la hiérarchie de vues est normalement renvoyée.
Cependant, vous pouvez remplacer hitTest:withEvent
pour faire quelque chose différemment. Dans de nombreux cas, pointInside:withEvent:
est plus simple et fournit toujours suffisamment d’options pour modifier la gestion des événements dans votre application.
Je pense que vous confondez le sous-classement avec la hiérarchie des vues. Ce que dit le doc est comme suit. Disons que vous avez cette hiérarchie de vue. Par hiérarchie, je ne parle pas de la hiérarchie des classes, mais des vues dans la hiérarchie des vues, comme suit:
+----------------------------+ |A | |+--------+ +------------+ | ||B | |C | | || | |+----------+| | |+--------+ ||D || | | |+----------+| | | +------------+ | +----------------------------+
Dis que tu mets ton doigt dans D
Voici ce qui va arriver:
hitTest:withEvent:
est appelé sur A
, la vue la plus haute de la hiérarchie de vues. pointInside:withEvent:
est appelé récursivement sur chaque vue.
pointInside:withEvent:
est appelé sur A
et renvoie YES
pointInside:withEvent:
est appelé sur B
et renvoie NO
pointInside:withEvent:
est appelé sur C
et renvoie YES
pointInside:withEvent:
est appelé sur D
et renvoie YES
YES
, la hiérarchie verra la sous-vue sur laquelle le toucher a eu lieu. Dans ce cas, à partir de A
, C
et D
, ce sera D
D
sera la vue du hit-test Je trouve que ce test Hit dans iOS est très utile
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) { return nil; } if ([self pointInside:point withEvent:event]) { for (UIView *subview in [self.subviews reverseObjectEnumerator]) { CGPoint convertedPoint = [subview convertPoint:point fromView:self]; UIView *hitTestView = [subview hitTest:convertedPoint withEvent:event]; if (hitTestView) { return hitTestView; } } return self; } return nil; }
Modifier Swift 4:
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { if self.point(inside: point, with: event) { return super.hitTest(point, with: event) } guard isUserInteractionEnabled, !isHidden, alpha > 0 else { return nil } for subview in subviews.reversed() { let convertedPoint = subview.convert(point, from: self) if let hitView = subview.hitTest(convertedPoint, with: event) { return hitView } } return nil }
Merci pour les réponses, ils m’ont aidé à résoudre la situation avec des vues “superposées”.
+----------------------------+ |A +--------+ | | |B +------------------+ | | | |CX | | | | +------------------+ | | | | | | +--------+ | | | +----------------------------+
Supposons que l’utilisateur touche X
pointInside:withEvent:
on B
renvoie NO
, donc hitTest:withEvent:
renvoie A
J’ai écrit la catégorie sur UIView
pour gérer le problème lorsque vous devez recevoir le contact sur la vue la plus visible .
- (UIView *)overlapHitTest:(CGPoint)point withEvent:(UIEvent *)event { // 1 if (!self.userInteractionEnabled || [self isHidden] || self.alpha == 0) return nil; // 2 UIView *hitView = self; if (![self pointInside:point withEvent:event]) { if (self.clipsToBounds) return nil; else hitView = nil; } // 3 for (UIView *subview in [self.subviewsreverseObjectEnumerator]) { CGPoint insideSubview = [self convertPoint:point toView:subview]; UIView *sview = [subview overlapHitTest:insideSubview withEvent:event]; if (sview) return sview; } // 4 return hitView; }
userInteractionEnabled
défini sur NO
; self
intérieur de self
, le self
sera considéré comme un résultat potentiel. Remarque: [self.subviewsreverseObjectEnumerator]
devait suivre la hiérarchie de vue du haut vers le bas. Et vérifiez les clipsToBounds
pour vous assurer de ne pas tester les sous-vues masquées.
Usage:
hitTest:withEvent:
avec ceci - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { return [self overlapHitTest:point withEvent:event]; }
Le Guide officiel d’Apple fournit également de bonnes illustrations.
J’espère que cela aide quelqu’un.
Cela se voit comme cet extrait!
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { if (self.hidden || !self.userInteractionEnabled || self.alpha < 0.01) { return nil; } if (![self pointInside:point withEvent:event]) { return nil; } __block UIView *hitView = self; [self.subViews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { CGPoint thePoint = [self convertPoint:point toView:obj]; UIView *theSubHitView = [obj hitTest:thePoint withEvent:event]; if (theSubHitView != nil) { hitView = theSubHitView; *stop = YES; } }]; return hitView; }
L’extrait de @lion fonctionne comme un charme. Je l’ai porté sur swift 2.1 et l’ai utilisé comme une extension d’UIView. Je le poste ici au cas où quelqu’un en aurait besoin.
extension UIView { func overlapHitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { // 1 if !self.userInteractionEnabled || self.hidden || self.alpha == 0 { return nil } //2 var hitView: UIView? = self if !self.pointInside(point, withEvent: event) { if self.clipsToBounds { return nil } else { hitView = nil } } //3 for subview in self.subviews.reverse() { let insideSubview = self.convertPoint(point, toView: subview) if let sview = subview.overlapHitTest(insideSubview, withEvent: event) { return sview } } return hitView } }
Pour l’utiliser, il suffit de remplacer hitTest: point: withEvent dans votre uiview comme suit:
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { let uiview = super.hitTest(point, withEvent: event) print("hittest",uiview) return overlapHitTest(point, withEvent: event) }