Fondamentalement, j'essaie d'appeler la vue de mon contrôleur viewWillAppear lorsque ma vue est rejetée. La vue est rejetée par la vue elle-même qui détecte un tap et appelle [self removeFromSuperview]; ViewController n'appelle pas viewWillAppear / WillDisappear / DidAppear / DidDisappear lui-même.
mahboudz
Je voulais dire que j'essaye d'appeler viewWillDisappear car ma vue est rejetée.
Oui, superviewc'est la vue qui contient votre vue. Votre vue ne doit pas savoir quel est exactement son contrôleur de vue, car cela enfreindrait les principes MVC.
Le contrôleur, quant à lui, sait de quelle vue il est responsable ( self.view = myView), et généralement, cette vue délègue des méthodes / événements à gérer au contrôleur.
En règle générale, au lieu d'un pointeur vers votre vue, vous devriez avoir un pointeur vers votre contrôleur, qui à son tour peut soit exécuter une logique de contrôle, soit transmettre quelque chose à sa vue.
Je ne sais pas si cela enfreindrait les principes MVC. À tout moment, une vue n'a qu'un seul contrôleur de vue. Pouvoir y accéder pour lui renvoyer un message devrait être une fonctionnalité automatique, et non une fonctionnalité pour laquelle vous devez travailler (en ajoutant une propriété pour garder une trace). On pourrait dire la même chose des vues: pourquoi avez-vous besoin de savoir qui vous êtes l'enfant? Ou s'il existe d'autres points de vue frères et sœurs. Pourtant, il existe des moyens d'obtenir ces objets.
mahboudz le
Vous avez un peu raison sur la vue, connaissant son parent, ce n'est pas une décision de conception très claire, mais il est déjà établi pour effectuer certaines actions, en utilisant directement une variable de membre superview (vérifier le type de parent, supprimer du parent, etc.). Ayant récemment travaillé avec PureMVC, je suis devenu un peu plus pointilleux sur l'abstraction du design :) Je ferais un parallèle entre les classes UIView et UIViewController de l'iPhone et les classes View et Mediator de PureMVC - la plupart du temps, la classe View n'a pas besoin de connaître son gestionnaire / interface MVC (UIViewController / Mediator).
Dimitar Dimitrov le
9
Mot clé: "le plus".
Glenn Maynard
278
De la UIResponderdocumentation pour nextResponder:
La classe UIResponder ne stocke pas ou ne définit pas automatiquement le répondeur suivant, mais renvoie nil par défaut. Les sous-classes doivent remplacer cette méthode pour définir le prochain répondeur.UIView implémente cette méthode en retournant l'objet UIViewController qui le gère (s'il en a un) ou son superview (si ce n'est pas le cas) ; UIViewController implémente la méthode en renvoyant la supervision de sa vue; UIWindow renvoie l'objet application et UIApplication renvoie nil.
Ainsi, si vous recurse une vue nextResponderjusqu'à ce qu'elle soit de type UIViewController, alors vous avez le viewController parent de n'importe quelle vue.
Notez qu'il peut toujours pas avoir de contrôleur de vue parent. Mais seulement si la vue ne fait pas partie de la hiérarchie de vues d'un viewController.
Juste une chose: si vous vous inquiétez de la pollution de catégorie, définissez-la simplement comme une fonction statique plutôt que comme une macro. De plus, le typage brutal est dangereux, et en plus de cela, la macro peut ne pas être correcte mais je ne suis pas sûr.
mojuba
La macro fonctionne, je n'utilise que la version macro personnellement. Je démarre beaucoup de projets et j'ai un en-tête que je viens de déposer partout avec un tas de ces fonctions utilitaires macro. Cela me fait gagner du temps. Si vous n'aimez pas les macros, vous pouvez les adapter en fonction, mais les fonctions statiques semblent fastidieuses, car vous devez ensuite en mettre une dans chaque fichier que vous souhaitez utiliser. On dirait que vous voudriez plutôt une fonction non statique déclarée dans un en-tête et définie dans un .m quelque part?
mxcl
Bien pour éviter certains délégués ou notifications. Merci!
Ferran Maylinch
Vous devriez en faire une extension de UIResponder;). Article très édifiant.
ScottyBlades
32
@andrey répond en une ligne (testé dans Swift 4.1 ):
parentViewControllerne peut pas être défini publicsi l'extension est dans le même fichier que UIViewvous pouvez la définir fileprivate, elle compile mais cela ne fonctionne pas! 😐
Cela fonctionne bien. Je l'ai utilisé pour l'action du bouton retour dans le fichier .xib d'en-tête commun.
McDonal_11
23
À des fins de débogage uniquement, vous pouvez appeler _viewDelegate vues pour obtenir leurs contrôleurs de vue. C'est une API privée, donc pas sûre pour l'App Store, mais pour le débogage, c'est utile.
Autres méthodes utiles:
_viewControllerForAncestor- obtenir le premier contrôleur qui gère une vue dans la chaîne superview. (merci n00neimp0rtant)
_rootAncestorViewController - récupère le contrôleur ancêtre dont la hiérarchie de vue est actuellement définie dans la fenêtre.
C'est exactement pourquoi je suis venu à cette question. Apparemment, «nextResponder» fait la même chose, mais j'apprécie la perspicacité que cette réponse fournit. Je comprends et j'aime MVC, mais le débogage est un autre animal!
mbm29414
4
Il semble que cela ne fonctionne que sur la vue principale du contrôleur de vue, pas sur les sous-vues de celui-ci. _viewControllerForAncestorparcourra les super-vues jusqu'à ce qu'il trouve la première qui appartient à un contrôleur de vue.
n00neimp0rtant
Merci @ n00neimp0rtant! Je vote pour cette réponse pour que les gens voient votre commentaire.
eyuelt
Mettez à jour la réponse avec plus de méthodes.
Leo Natan
1
Ceci est utile lors du débogage.
evanchin le
10
Pour obtenir une référence à UIViewController ayant UIView, vous pouvez faire une extension de UIResponder (qui est une super classe pour UIView et UIViewController), ce qui permet de remonter dans la chaîne de répondeurs et d'atteindre ainsi UIViewController (sinon en retournant nil).
Je pense que vous pouvez propager le robinet au contrôleur de vue et le laisser le gérer. C'est une approche plus acceptable. En ce qui concerne l'accès à un contrôleur de vue depuis sa vue, vous devez conserver une référence à un contrôleur de vue, car il n'y a pas d'autre moyen. Voir ce fil, cela peut aider:
Accéder au contrôleur de vue à partir d'une vue
Si vous avez une poignée de vues et que l'on ferme toutes les vues et que vous devez appeler viewWillDisappear, ne serait-il pas plus facile pour cette vue de détecter le robinet que de remettre le robinet au contrôleur de vue et de faire vérifier le contrôleur de vue avec toutes les vues pour voir laquelle a été tapée?
Hélas, cela est impossible à moins que vous ne sous-classiez la vue et ne lui fournissiez une propriété d'instance ou similaire qui stocke la référence du contrôleur de vue à l'intérieur une fois que la vue est ajoutée à la scène ...
Dans la plupart des cas, il est très facile de contourner le problème d'origine de ce message car la plupart des contrôleurs de vue sont des entités bien connues du programmeur qui était responsable de l'ajout de toutes les sous-vues à la vue de ViewController ;-) C'est pourquoi je suppose qu'Apple jamais pris la peine d'ajouter cette propriété.
Réponses:
Oui,
superview
c'est la vue qui contient votre vue. Votre vue ne doit pas savoir quel est exactement son contrôleur de vue, car cela enfreindrait les principes MVC.Le contrôleur, quant à lui, sait de quelle vue il est responsable (
self.view = myView
), et généralement, cette vue délègue des méthodes / événements à gérer au contrôleur.En règle générale, au lieu d'un pointeur vers votre vue, vous devriez avoir un pointeur vers votre contrôleur, qui à son tour peut soit exécuter une logique de contrôle, soit transmettre quelque chose à sa vue.
la source
De la
UIResponder
documentation pournextResponder
:Ainsi, si vous recurse une vue
nextResponder
jusqu'à ce qu'elle soit de typeUIViewController
, alors vous avez le viewController parent de n'importe quelle vue.Notez qu'il peut toujours pas avoir de contrôleur de vue parent. Mais seulement si la vue ne fait pas partie de la hiérarchie de vues d'un viewController.
Swift 3 et Extension Swift 4.1 :
Extension Swift 2:
Catégorie Objectif-C:
Cette macro évite la pollution de catégorie:
la source
UIResponder
;). Article très édifiant.@andrey répond en une ligne (testé dans Swift 4.1 ):
usage:
la source
parentViewController
ne peut pas être définipublic
si l'extension est dans le même fichier queUIView
vous pouvez la définirfileprivate
, elle compile mais cela ne fonctionne pas! 😐À des fins de débogage uniquement, vous pouvez appeler
_viewDelegate
vues pour obtenir leurs contrôleurs de vue. C'est une API privée, donc pas sûre pour l'App Store, mais pour le débogage, c'est utile.Autres méthodes utiles:
_viewControllerForAncestor
- obtenir le premier contrôleur qui gère une vue dans la chaîne superview. (merci n00neimp0rtant)_rootAncestorViewController
- récupère le contrôleur ancêtre dont la hiérarchie de vue est actuellement définie dans la fenêtre.la source
_viewControllerForAncestor
parcourra les super-vues jusqu'à ce qu'il trouve la première qui appartient à un contrôleur de vue.Pour obtenir une référence à UIViewController ayant UIView, vous pouvez faire une extension de UIResponder (qui est une super classe pour UIView et UIViewController), ce qui permet de remonter dans la chaîne de répondeurs et d'atteindre ainsi UIViewController (sinon en retournant nil).
la source
La manière rapide et générique dans Swift 3:
la source
Si vous n'êtes pas familier avec le code et que vous souhaitez trouver ViewController correspondant à une vue donnée, vous pouvez essayer:
Dans la plupart des cas, vous obtiendrez UIView, mais de temps en temps, il y aura une classe basée sur UIViewController.
la source
Je pense que vous pouvez propager le robinet au contrôleur de vue et le laisser le gérer. C'est une approche plus acceptable. En ce qui concerne l'accès à un contrôleur de vue depuis sa vue, vous devez conserver une référence à un contrôleur de vue, car il n'y a pas d'autre moyen. Voir ce fil, cela peut aider: Accéder au contrôleur de vue à partir d'une vue
la source
Plus de code de sécurité de type pour Swift 3.0
la source
Hélas, cela est impossible à moins que vous ne sous-classiez la vue et ne lui fournissiez une propriété d'instance ou similaire qui stocke la référence du contrôleur de vue à l'intérieur une fois que la vue est ajoutée à la scène ...
Dans la plupart des cas, il est très facile de contourner le problème d'origine de ce message car la plupart des contrôleurs de vue sont des entités bien connues du programmeur qui était responsable de l'ajout de toutes les sous-vues à la vue de ViewController ;-) C'est pourquoi je suppose qu'Apple jamais pris la peine d'ajouter cette propriété.
la source
Un peu tard, mais voici une extension qui vous permet de trouver un répondeur de tout type, y compris ViewController.
la source
Si vous définissez un point d'arrêt, vous pouvez le coller dans le débogueur pour imprimer la hiérarchie des vues:
Vous devriez pouvoir trouver le parent de votre vue quelque part dans ce désordre :)
la source
recursiveDescription
imprime uniquement la hiérarchie des vues , pas les contrôleurs de vue.