164 votes

Étant donné une vue, comment puis-je obtenir son viewController ?

J'ai un pointeur sur un UIView . Comment puis-je accéder à son UIViewController ? [self superview] est un autre UIView mais pas le UIViewController n'est-ce pas ?

0 votes

Je pense que ce fil de discussion a les réponses : Accéder à UIViewController depuis UIView sur iPhone ?

0 votes

En fait, j'essaie d'appeler la fonction viewWillAppear de mon viewController lorsque ma vue est rejetée. La vue est renvoyée par la vue elle-même qui détecte un tap et appelle [self removeFromSuperview] ; le viewController n'appelle pas viewWillAppear/WillDisappear/DidAppear/DidDisappear lui-même.

0 votes

Je voulais dire que j'essaie d'appeler viewWillDisappear lorsque ma vue est rejetée.

317voto

mxcl Points 5921

De la UIResponder la documentation pour nextResponder :

La classe UIResponder ne stocke pas ou ne définit pas automatiquement le prochain répondeur, mais renvoie nil par défaut. Les sous-classes doivent surcharger 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 sa superview (s'il n'en a pas). UIViewController implémente la méthode en renvoyant la vue supérieure de sa vue ; UIWindow renvoie l'objet d'application, et UIApplication renvoie nil.

Ainsi, si vous effectuez une récursion dans la vue nextResponder jusqu'à ce qu'il soit de type UIViewController alors vous avez le viewController parent de n'importe quelle vue.

Notez qu'il est toujours possible pas ont un contrôleur de vue parent. Mais seulement si la vue ne fait pas partie de la hiérarchie des vues d'un viewController.

Swift 3 et Swift 4.1 extension :

extension UIView {
    var parentViewController: UIViewController? {
        var parentResponder: UIResponder? = self
        while parentResponder != nil {
            parentResponder = parentResponder?.next
            if let viewController = parentResponder as? UIViewController {
                return viewController
            }
        }
        return nil
    }
}

Extension de Swift 2 :

extension UIView {
    var parentViewController: UIViewController? {
        var parentResponder: UIResponder? = self
        while parentResponder != nil {
            parentResponder = parentResponder!.nextResponder()
            if let viewController = parentResponder as? UIViewController {
                return viewController
            }
        }
        return nil
    }
}

Catégorie Objective-C :

@interface UIView (mxcl)
- (UIViewController *)parentViewController;
@end

@implementation UIView (mxcl)
- (UIViewController *)parentViewController {
    UIResponder *responder = self;
    while ([responder isKindOfClass:[UIView class]])
        responder = [responder nextResponder];
    return (UIViewController *)responder;
}
@end

Cette macro évite la pollution des catégories :

#define UIViewParentController(__view) ({ \
    UIResponder *__responder = __view; \
    while ([__responder isKindOfClass:[UIView class]]) \
        __responder = [__responder nextResponder]; \
    (UIViewController *)__responder; \
})

0 votes

Une seule chose : si vous vous inquiétez de la pollution par catégorie, définissez-la simplement comme une fonction statique plutôt que comme une macro. De plus, le typecast brutal est dangereux, et en plus de cela, la macro peut ne pas être correcte, mais je n'en suis pas sûr.

0 votes

La macro fonctionne, je n'utilise personnellement que la version macro. Je commence beaucoup de projets et j'ai un en-tête que je dépose partout avec un tas de ces fonctions utilitaires de macro. Ça me fait gagner du temps. Si vous n'aimez pas les macros, vous pouvez l'adapter en fonction, mais les fonctions statiques semblent fastidieuses, car vous devez en mettre une dans chaque fichier que vous voulez utiliser. Il semblerait qu'il faille plutôt déclarer une fonction non statique dans un en-tête et la définir dans un .m quelque part ?

0 votes

C'est bien d'éviter certains délégués ou notifications. Merci !

44voto

Dimitar Dimitrov Points 6587

Oui, le superview est la vue qui contient votre vue. Votre vue ne doit pas savoir quel est exactement son contrôleur de vue, car cela briserait 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 les méthodes/événements à traiter au contrôleur.

En général, 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 passer quelque chose à sa vue.

26 votes

Je ne suis pas sûr que cela enfreigne les principes MVC. À tout moment, une vue n'a qu'un seul contrôleur de vue. Être capable d'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 la trace). On pourrait dire la même chose des vues : pourquoi avez-vous besoin de savoir quel enfant vous êtes ? Ou s'il existe d'autres vues de frères et sœurs. Pourtant, il existe des moyens d'obtenir ces objets.

0 votes

Vous avez quelque peu raison au sujet de la vue qui connaît son parent, ce n'est pas une décision de conception très claire, mais il est déjà établi de faire certaines actions, en utilisant directement une variable membre de la vue supérieure (vérifier le type de parent, enlever du parent, etc.). Ayant travaillé avec PureMVC récemment, je suis devenu un peu plus pointilleux sur l'abstraction de la conception :) 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 handler/interface MVC (UIViewController/Mediator).

25voto

Leo Natan Points 25262

À des fins de débogage uniquement, vous pouvez appeler _viewDelegate sur les vues pour obtenir leurs contrôleurs de vues. Il s'agit d'une API privée, donc pas sûre pour l'App Store, mais utile pour le débogage.

Autres méthodes utiles :

  • _viewControllerForAncestor - obtenir le premier contrôleur qui gère une vue dans la chaîne des supervisions. (merci n00neimp0rtant)
  • _rootAncestorViewController - obtenir le contrôleur ancêtre dont la la hiérarchie des vues est définie dans la fenêtre actuelle.

0 votes

C'est exactement la raison pour laquelle je suis venu à cette question. Apparemment, "nextResponder" fait la même chose, mais j'apprécie l'éclairage apporté par cette réponse. Je comprends et j'aime MVC, mais le débogage est un autre animal !

4 votes

Il semble que cela ne fonctionne que sur la vue principale du contrôleur de vue, et non sur ses sous-vues. _viewControllerForAncestor va parcourir les supervisions jusqu'à ce qu'il trouve la première qui appartient à un contrôleur de vue.

0 votes

Merci @n00neimp0rtant ! Je mets cette réponse en upvoting pour que les gens voient ton commentaire.

4voto

Ushox Points 598

Je pense que ce fil de discussion a les réponses :

Accéder à UIViewController depuis UIView sur iPhone ?

-1voto

Scott McCoy Points 54

Si vous définissez un point d'arrêt, vous pouvez le coller dans le débogueur pour imprimer la hiérarchie des vues :

po [[UIWindow keyWindow] recursiveDescription]

Vous devriez être en mesure de trouver le parent de votre vue quelque part dans ce fouillis :)

0 votes

recursiveDescription n'imprime que le voir et non les contrôleurs de vues.

1 votes

à partir de cela vous pouvez identifier le viewcontroller @AlanZeino

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