Existe-t-il un moyen simple d'autoriser l'interaction avec un bouton dans un UIView qui se trouve sous un autre UIView - où il n'y a pas d'objets réels de l'UIView supérieur au-dessus du bouton?
Par exemple, en ce moment j'ai un UIView (A) avec un objet en haut et un objet en bas de l'écran et rien au milieu. Cela se trouve au-dessus d'un autre UIView qui a des boutons au milieu (B). Cependant, je n'arrive pas à interagir avec les boutons au milieu de B.
Je peux voir les boutons de B - j'ai défini l'arrière-plan de A sur clearColor - mais les boutons de B ne semblent pas recevoir de touches malgré le fait qu'il n'y a pas d'objets de A réellement au-dessus de ces boutons.
EDIT - Je veux toujours pouvoir interagir avec les objets dans le haut UIView
Il existe sûrement un moyen simple de faire cela?
la source
UIButton
qui est sous un semi-transparentUIView
tandis que la partie non transparente duUIView
répondra toujours aux événements tactiles.Réponses:
Vous devez créer une sous-classe UIView pour votre vue de dessus et remplacer la méthode suivante:
Vous pouvez également consulter la méthode hitTest: event:.
la source
return
fait cette déclaration, mais celareturn CGRectContainsPoint(eachSubview.frame, point)
fonctionne pour moi. Réponse extrêmement utile sinonMIDDLE_Y1<=y<=MIDDLE_Y2
zone.Bien que la plupart des réponses ici fonctionnent, je suis un peu surpris de voir que la réponse la plus pratique, générique et infaillible n'a pas été donnée ici. @Ash est venu le plus près, sauf qu'il se passe quelque chose d'étrange avec le retour du superview ... ne faites pas ça.
Cette réponse est tirée d'une réponse que j'ai donnée à une question similaire, ici .
[super hitTest:point withEvent:event]
renverra la vue la plus profonde de la hiérarchie de cette vue qui a été touchée. SihitView == self
(c'est-à-dire s'il n'y a pas de sous-vue sous le point de contact), retourneznil
, en spécifiant que cette vue ne doit pas recevoir le toucher. Le fonctionnement de la chaîne de répondeurs signifie que la hiérarchie des vues au-dessus de ce point continuera à être parcourue jusqu'à ce qu'une vue soit trouvée qui répondra au toucher. Ne retournez pas le superview, car ce n'est pas à cette vue si son superview doit accepter les touches ou non!Cette solution est:
pointInside:withEvent:
pour renvoyer une zone tactile particulière).Je l'utilise assez souvent pour que je l'ai résumé dans une sous-classe pour enregistrer des sous-classes de vue inutiles pour un remplacement. En prime, ajoutez une propriété pour la rendre configurable:
Alors devenez sauvage et utilisez cette vue partout où vous pourriez utiliser une plaine
UIView
. Sa configuration est aussi simple que la miseonlyRespondToTouchesInSubviews
àYES
.la source
Il existe plusieurs façons de gérer cela. Mon préféré est de remplacer hitTest: withEvent: dans une vue qui est une vue commune (peut-être indirectement) aux vues en conflit (on dirait que vous les appelez A et B). Par exemple, quelque chose comme ceci (ici A et B sont des pointeurs UIView, où B est celui "caché", qui est normalement ignoré):
Vous pouvez également modifier la
pointInside:withEvent:
méthode comme le suggère gyim. Cela vous permet d'obtenir essentiellement le même résultat en «faisant un trou» dans A, au moins pour les touches.Une autre approche est le transfert d'événement, ce qui signifie
touchesBegan:withEvent:
des méthodes de substitution et similaires (commetouchesMoved:withEvent:
etc.) pour envoyer des touches à un objet différent de celui où elles vont d'abord. Par exemple, en A, vous pouvez écrire quelque chose comme ceci:Cependant, cela ne fonctionnera pas toujours comme vous le souhaitez! L'essentiel est que les contrôles intégrés comme UIButton ignorent toujours les touchers transférés. Pour cette raison, la première approche est plus fiable.
Il y a un bon article de blog expliquant tout cela plus en détail, ainsi qu'un petit projet xcode fonctionnel pour faire une démonstration des idées, disponible ici:
http://bynomial.com/blog/?p=74
la source
Vous devez régler
upperView.userInteractionEnabled = NO;
, sinon la vue supérieure interceptera les touches.La version Interface Builder de ceci est une case à cocher en bas du panneau Attributs de vue appelée "Interaction utilisateur activée". Décochez-le et vous devriez être prêt à partir.
la source
Implémentation personnalisée de pointInside: withEvent: en effet, cela semblait être la voie à suivre, mais gérer des coordonnées codées en dur me semblait étrange. J'ai donc fini par vérifier si le CGPoint était à l'intérieur du bouton CGRect en utilisant la fonction CGRectContainsPoint ():
la source
Dernièrement, j'ai écrit un cours qui m'aidera avec cela. L'utiliser comme classe personnalisée pour un
UIButton
ouUIView
passera les événements tactiles qui ont été exécutés sur un pixel transparent.Cette solution est un peu meilleure que la réponse acceptée car vous pouvez toujours cliquer sur un
UIButton
qui est sous un semi-transparentUIView
tandis que la partie non transparente duUIView
répondra toujours aux événements tactiles.Comme vous pouvez le voir dans le GIF, le bouton Girafe est un simple rectangle, mais les événements tactiles sur les zones transparentes sont transmis au jaune en
UIButton
dessous.Lien vers la classe
la source
Je suppose que je suis un peu en retard à cette soirée, mais j'ajouterai cette solution possible:
Si vous utilisez ce code pour remplacer la fonction hitTest standard d'un UIView personnalisé, il ignorera UNIQUEMENT la vue elle-même. Toutes les sous-vues de cette vue renverront leurs hits normalement, et tous les hits qui seraient allés à la vue elle-même sont transmises à sa supervision.
-Cendre
la source
[self superview]
. la documentation sur cette méthode state "Retourne le descendant le plus éloigné du récepteur dans la hiérarchie de vue (y compris lui-même) qui contient un point spécifié" et "Renvoie nil si le point se trouve complètement en dehors de la hiérarchie de vue du récepteur". je pense que vous devriez revenirnil
. lorsque vous retournez nil, le contrôle passera à la supervision pour qu'elle vérifie si elle a des résultats ou non. Donc, fondamentalement, il fera la même chose, sauf que le retour de superview pourrait casser quelque chose dans le futur.Je viens de riffer la réponse acceptée et de la mettre ici pour ma référence. La réponse acceptée fonctionne parfaitement. Vous pouvez l'étendre comme ceci pour permettre aux sous-vues de votre vue de recevoir le toucher, OU la transmettre à toutes les vues derrière nous:
Remarque: vous n'avez même pas besoin de faire de la récursivité sur l'arborescence des sous-vues, car chaque
pointInside:withEvent:
méthode gérera cela pour vous.la source
La désactivation de la propriété userInteraction peut aider. Par exemple:
(Remarque: dans le code ci-dessus, 'self' fait référence à une vue)
De cette façon, vous ne pouvez afficher que sur topView, mais vous n'obtiendrez pas d'entrées utilisateur. Toutes ces touches utilisateur passeront par cette vue et la vue de dessous répondra à leur place. J'utiliserais ce topView pour afficher des images transparentes ou pour les animer.
la source
Cette approche est assez propre et permet que les sous- vues transparentes ne réagissent pas non plus aux touches. Sous-classez simplement
UIView
et ajoutez la méthode suivante à son implémentation:la source
Ma solution ici:
J'espère que cela t'aides
la source
Vous pouvez faire quelque chose pour intercepter le toucher dans les deux vues.
Vue de dessus:
Mais c'est l'idée.
la source
Voici une version Swift:
la source
Swift 3
la source
Je n'ai jamais construit une interface utilisateur complète à l'aide de la boîte à outils de l'interface utilisateur, donc je n'ai pas beaucoup d'expérience avec elle. Voici ce que je pense devrait fonctionner.
Chaque UIView, et cette UIWindow, a une propriété
subviews
, qui est un NSArray contenant toutes les sous-vues.La première sous-vue que vous ajoutez à une vue recevra l'index 0, et l'index suivant 1 et ainsi de suite. Vous pouvez également remplacer
addSubview:
parinsertSubview: atIndex:
ouinsertSubview:aboveSubview:
et de telles méthodes qui peuvent déterminer la position de votre sous-vue dans la hiérarchie.Vérifiez donc votre code pour voir quelle vue vous ajoutez en premier à votre UIWindow. Ce sera 0, l'autre sera 1.
Maintenant, à partir de l'une de vos sous-vues, pour en atteindre une autre, procédez comme suit:
Faites-moi savoir si cela fonctionne pour votre cas!
(sous ce marqueur se trouve ma réponse précédente):
Si les vues doivent communiquer entre elles, elles doivent le faire via un contrôleur (c'est-à-dire en utilisant le modèle MVC populaire ).
Lorsque vous créez une nouvelle vue, vous pouvez vous assurer qu'elle s'enregistre avec un contrôleur.
La technique consiste donc à vous assurer que vos vues s'inscrivent auprès d'un contrôleur (qui peut les stocker par nom ou selon votre préférence dans un dictionnaire ou un tableau). Soit vous pouvez demander au contrôleur d'envoyer un message pour vous, soit vous pouvez obtenir une référence à la vue et communiquer directement avec elle.
Si votre vue n'a pas de lien vers le contrôleur (ce qui peut être le cas), vous pouvez utiliser des singletons et / ou des méthodes de classe pour obtenir une référence à votre contrôleur.
la source
Je pense que la bonne façon est d'utiliser la chaîne de vues intégrée à la hiérarchie des vues. Pour vos sous-vues qui sont poussées sur la vue principale, n'utilisez pas l'UIView générique, mais sous-classez UIView (ou l'une de ses variantes comme UIImageView) pour créer MYView: UIView (ou le supertype de votre choix, tel que UIImageView). Dans l'implémentation de YourView, implémentez la méthode touchesBegan. Cette méthode sera alors appelée lorsque cette vue est touchée. Tout ce dont vous avez besoin dans cette implémentation est une méthode d'instance:
this touchesBegan est une API de répondeur, vous n'avez donc pas besoin de le déclarer dans votre interface publique ou privée; c'est l'une de ces API magiques que vous devez juste connaître. Ce self.superview fera remonter la requête éventuellement au viewController. Dans viewController, alors, implémentez ce touchBegan pour gérer le toucher.
Notez que l'emplacement des touches (CGPoint) est automatiquement ajusté par rapport à la vue englobante pour vous au fur et à mesure qu'il rebondit dans la chaîne de hiérarchie de vues.
la source
Je veux juste poster ceci, car j'ai eu un problème quelque peu similaire, j'ai passé beaucoup de temps à essayer de mettre en œuvre des réponses ici sans aucune chance. Ce que j'ai fini par faire:
et la mise en œuvre
UIGestureRecognizerDelegate
:La vue de dessous était un contrôleur de navigation avec un certain nombre de segues et j'avais une sorte de porte au-dessus qui pouvait se fermer avec un geste panoramique. Le tout était intégré dans un autre VC. A fonctionné comme un charme. J'espère que cela t'aides.
la source
Implémentation Swift 4 pour une solution basée sur HitTest
la source
Dérivé de l'excellente réponse de Stuart, et pour la plupart infaillible, et de l'implémentation utile de Segev, voici un package Swift 4 que vous pouvez ajouter à n'importe quel projet:
Et puis avec hitTest:
la source