102 votes

Capture des contacts sur une sous-vue en dehors du cadre de sa vue supérieure à l'aide de hitTest:withEvent :

Mon problème : J'ai une vue supérieure EditView qui occupe pratiquement tout le cadre de l'application, et une sous-vue MenuView qui n'occupe que les ~20% du bas, et ensuite MenuView contient sa propre sous-vue ButtonView qui réside en fait en dehors de MenuView (quelque chose comme ceci : ButtonView.frame.origin.y = -100 ).

(note : EditView possède d'autres sous-vues qui ne font pas partie de l'ensemble de l'UE. MenuView de la hiérarchie des vues, mais peut affecter la réponse).

Vous connaissez probablement déjà le problème : lorsque ButtonView est dans les limites de MenuView (ou, plus précisément, lorsque mes touches sont à l'intérieur de MenuView ), ButtonView réagit aux événements liés au toucher. Lorsque mes touchers sont en dehors de MenuView (mais toujours dans les limites de la ButtonView ), aucun événement de contact n'est reçu par l'utilisateur. ButtonView .

Exemple :

  • (E) est EditView le parent de toutes les vues
  • (M) est MenuView une sous-vue de EditView
  • (B) est ButtonView une sous-vue de MenuView

Diagramme :

+------------------------------+
|E                             |
|                              |
|                              |
|                              |
|                              |
|+-----+                       |
||B    |                       |
|+-----+                       |
|+----------------------------+|
||M                           ||
||                            ||
|+----------------------------+|
+------------------------------+

Comme (B) est en dehors du cadre de (M), un toucher dans la région de (B) ne sera jamais envoyé à (M) - en fait, (M) n'analyse jamais le toucher dans ce cas, et le toucher est envoyé à l'objet suivant dans la hiérarchie.

Objectif : J'en déduis que le fait d'outrepasser hitTest:withEvent: peut résoudre ce problème, mais je ne comprends pas exactement comment. Dans mon cas, faut-il hitTest:withEvent: être remplacée dans EditView (ma vue supérieure "maître") ? Ou doit-elle être surchargée dans MenuView la vue supérieure directe du bouton qui ne reçoit pas de touches ? Ou est-ce que je ne vois pas les choses de la bonne façon ?

Si cela nécessite une longue explication, une bonne ressource en ligne serait utile - à l'exception des documents UIView d'Apple, qui n'ont pas été clairs pour moi.

Gracias.

0voto

Sandy Points 412

Placez les lignes de code ci-dessous dans votre hiérarchie de vues :

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
    UIView* hitView = [super hitTest:point withEvent:event];
    if (hitView != nil)
    {
        [self.superview bringSubviewToFront:self];
    }
    return hitView;
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event
{
    CGRect rect = self.bounds;
    BOOL isInside = CGRectContainsPoint(rect, point);
    if(!isInside)
    {
        for (UIView *view in self.subviews)
        {
            isInside = CGRectContainsPoint(view.frame, point);
            if(isInside)
                break;
        }
    }
    return isInside;
}

Pour plus de précisions, cela a été expliqué dans mon blog : "goaheadwithiphonetech" concernant "Custom callout : Button is not clickable issue".

J'espère que cela vous aidera... ! !!

0 votes

Votre blog a été supprimé, alors où pouvons-nous trouver l'explication s'il vous plaît ?

0voto

Sonny Points 93

Si quelqu'un en a besoin, voici l'alternative rapide.

override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
    if !self.clipsToBounds && !self.hidden && self.alpha > 0 {
        for subview in self.subviews.reverse() {
            let subPoint = subview.convertPoint(point, fromView:self);

            if let result = subview.hitTest(subPoint, withEvent:event) {
                return result;
            }
        }
    }

    return nil
}

0voto

Jerem Lachkar Points 497

J'ai pris le temps de comprendre comment cela fonctionne exactement parce que les solutions ci-dessus n'ont pas fonctionné pour moi car j'ai beaucoup de sous-vues imbriquées. Je vais essayer d'expliquer simplement hitTest pour que chacun puisse adapter son code en fonction de la situation.

Imaginez la situation suivante : Une vue appelée GrandParent a une sous-vue entièrement dans ses limites appelée Parent. Cette vue Parent a une vue secondaire appelée Child qui a des limites en dehors de celles de Parent :

-------------------------------------- -> Grand parent 
|              ------- -> Child      |
|              |     |               |
|          ----|-----|--- -> Parent  |
|          |   |   ^ |   |           |
|          |   |     |   |           |
|          ----|-----|---            |
|              | +   |               |
|     *        |-----|               |
|                                    |
--------------------------------------

* + y ^ sont 3 touches utilisateur différentes. + Le toucher n'est pas reconnu sur l'enfant Chaque fois que vous touchez le grand parent n'importe où dans ses limites, le grand parent va appeler toutes ses sous-vues directes .hitTest peu importe où vous avez touché le grand parent . Donc ici, pour les 3 touches, le grand parent appellera parent.hitTest() .

hitTest est censé retourner le descendant le plus éloigné qui contient le point donné (le point est donné par rapport aux limites de soi, dans notre exemple, le Grand Parent appelle Parent.hitTest() avec un point relatif aux limites du Parent).

Para * touch, parent.hitTest renvoie nil, et c'est parfait. Pour ^ touch, parent.hitTest renvoie l'enfant, car il est dans ses limites (implémentation par défaut de hitTest).

Mais pour + touch, parent.hitTest renvoie nil par défaut, car la touche ne se trouve pas dans les limites de parent. Nous devons donc avoir notre propre implémentation de hitTest, afin de convertir le point relatif aux limites du parent en un point relatif aux limites de l'enfant. Ensuite, il faut appeler hitTest sur l'enfant pour voir si la touche se trouve dans les limites de l'enfant (l'enfant est censé avoir l'implémentation par défaut de hitTest, renvoyant le descendant le plus éloigné de lui dans ses limites, c'est-à-dire : lui-même).

Si vous avez des sous-vues imbriquées complexes et si la solution ci-dessus ne fonctionne pas très bien pour vous, cette explication peut être utile pour réaliser votre implémentation personnalisée, adaptée à votre hiérarchie de vues.

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