81 votes

modal Vue contrôleurs - comment afficher et de rejeter

Je suis en rupture de ma tête pour la dernière semaine sur la façon de résoudre le problème avec les montrant et en les rejetant de vue multiples sur les contrôleurs. J'ai créé un exemple de projet et de coller le code directement dans le projet. J'ai 3 contrôleurs de vue avec leurs correspondants .xib fichiers. MainViewController, VC1 et VC2. J'ai deux boutons sur l'écran principal du contrôleur.

- (IBAction)VC1Pressed:(UIButton *)sender
{
    VC1 *vc1 = [[VC1 alloc] initWithNibName:@"VC1" bundle:nil];
    [vc1 setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
    [self presentViewController:vc1 animated:YES completion:nil];
}

Cela ouvre VC1 sans problèmes. Dans le VC1, j'ai un autre bouton qui doit ouvrir VC2 alors que dans le même temps rejeter VC1.

- (IBAction)buttonPressedFromVC1:(UIButton *)sender
{
    VC2 *vc2 = [[VC2 alloc] initWithNibName:@"VC2" bundle:nil];
    [vc2 setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
    [self presentViewController:vc2 animated:YES completion:nil];
    [self dismissViewControllerAnimated:YES completion:nil];
} // This shows a warning: Attempt to dismiss from view controller <VC1: 0x715e460> while a presentation or dismiss is in progress!


- (IBAction)buttonPressedFromVC2:(UIButton *)sender
{
    [self dismissViewControllerAnimated:YES completion:nil];
} // This is going back to VC1. 

Je veux revenir à l'écran principal contrôleur alors que dans le même temps, VC1 doit avoir été supprimée de la mémoire pour de bon. VC1 devrait ne s'affiche quand je clique sur le VC1 bouton sur le contrôleur principal.

L'autre bouton sur l'écran Principal contrôleur doit également être en mesure d'afficher VC2 directement sans passer par VC1 et devrait revenir au contrôleur principal lorsqu'un bouton est cliqué sur VC2. Il n'y a pas de délai d'exécution du code, des boucles ou des minuteries. Juste à nu de l'os des appels pour afficher les contrôleurs.

S'il vous plaît aider.

188voto

foundry Points 15423

Cette ligne:

    [self dismissViewControllerAnimated:YES completion:nil];

n'est-ce pas l'envoi d'un message à lui-même, c'est en fait l'envoi d'un message à la présentation de VC, lui demandant de faire le rejetant. Lorsque vous présentez un VC, vous créez une relation entre la présentation de VC et de la présenté un. Donc, vous ne devez pas détruire la présentation de CR alors qu'il présente (les VC ne pouvez pas envoyer que de rejeter un message de retour...). Comme vous n'êtes pas vraiment en tenant compte de cela, vous quittez l'application dans un état de grande confusion. Voir ma réponse ici: Rejetant une Présenté-Vue-Contrôleur dans laquelle je recommande cette méthode est plus clairement écrit:

    [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];

Dans votre cas, vous devez vous assurer que tous les contrôle se fait en MainViewController. Vous devez utiliser un délégué d'envoyer le bon message à MainViewController de ViewController1, de sorte que MainViewController peut rejeter VC1 et puis de présenter VC2.

Dans VC2 VC1 ajouter un protocole dans votre .h fichier au-dessus de la @interface:

@protocol ViewController1Protocol <NSObject>

    - (void)dismissAndPresentVC2;

 @end

et plus bas, dans le même fichier dans le @interface de la section déclarer une propriété de tenir le délégué pointeur:

@property (nonatomic,weak) id <ViewController1Protocol> delegate;

Dans le VC1 .m de fichier, le bouton rejeter la méthode à appeler la méthode du délégué

    - (IBAction)buttonPressedFromVC1:(UIButton *)sender {
        [self.delegate dissmissAndPresentVC2]
        }

Maintenant dans MainViewController, la définir comme VC1 son délégué lors de la création de VC1:

- (IBAction)present1:(id)sender {
        ViewController1* vc = [[ViewController1 alloc] initWithNibName:@"ViewController1" bundle:nil];
    vc.delegate = self;
    [self present:vc];
}

et de mettre en œuvre la méthode du délégué:

- (void)dismissAndPresent2
{
    [self dismissViewControllerAnimated:NO completion:
     ^{
         [self present2:nil];
     }];
}

present2: peut être la même méthode que votre VC2Pressed: bouton IBAction méthode. Notez qu'il est appelé à partir de la fin du bloc de s'assurer que VC2 n'est pas présentée jusqu'à ce que VC1 est entièrement rejeté.

Vous êtes en train de passer de VC1->VCMain->VC2 vous aurez probablement seulement l'un des transitions d'être animé.

mise à jour

Dans vos commentaires, vous exprimer de la surprise à la complexité nécessaire pour atteindre un semblant de quelque chose de simple. Je vous assure, cette délégation modèle est tellement centrale pour beaucoup d'Objective-C et Cocoa, et cet exemple est le plus simple que vous pouvez obtenir, que vous devriez vraiment faire l'effort d'être à l'aise avec elle.

Dans Apple - Vue-Contrôleur Guide de Programmation qu'ils ont ceci à dire:

Rejetant une Présenté-Vue-Contrôleur

Quand vient le temps de faire disparaître une présenté-vue-contrôleur, la meilleure approche est de permettre la présentation de-vue-contrôleur de les rejeter. En d'autres termes, chaque fois que possible, même à la vue du contrôleur qui a présenté le point de vue du contrôleur doit également prendre la responsabilité de le rejeter. Bien qu'il existe plusieurs techniques pour informer la présentation de-vue-contrôleur qu'il est présenté-vue-contrôleur doit être rejeté, la technique de choix est la délégation. Pour plus d'informations, voir "Utilisation de la Délégation de Communiquer avec d'Autres Contrôleurs."

Si vous pensez vraiment à travers ce que vous voulez atteindre et comment vous allez à ce sujet, vous vous rendrez compte que la messagerie de votre MainViewController à faire, tout le travail est la seule logique pour sortir étant donné que vous ne voulez pas utiliser un NavigationController. Si vous ne utilisez un NavController, en effet, vous êtes "déléguer", même si pas explicitement, à l'navController à faire tout le travail. Il doit y avoir quelque objet qui maintient une piste centrale de ce qui se passe avec votre CR de navigation, et vous avez besoin de certains moyens de communiquer avec elle, quoi que vous fassiez.

Dans la pratique, Apple conseils est un peu extrême... dans la plupart des cas, vous n'avez pas besoin de faire un délégué dédié et de la méthode, vous pouvez compter sur [self presentingViewController] dismissViewControllerAnimated: - c'est quand dans les cas comme le vôtre que vous voulez que votre rejetant avoir d'autres effets sur des objets distants que vous devez prendre soin.

Voici quelque chose que vous pourriez imaginer travailler sans tout le délégué de soucis...

- (IBAction)dismiss:(id)sender {
    [[self presentingViewController] dismissViewControllerAnimated:YES 
                                                        completion:^{
        [self.presentingViewController performSelector:@selector(presentVC2:) 
                                            withObject:nil];
    }];

}

Après avoir demandé à la présentation de contrôleur de rejeter nous, nous avons l'achèvement de l'édifice, qui appelle une méthode dans le presentingViewController d'invoquer VC2. Aucun délégué nécessaires. (Un grand point de vente de blocs est qu'elles permettent de réduire la nécessité pour les délégués dans ces circonstances). Toutefois, dans ce cas il y a quelques choses de façon...

  • en VC1 vous ne savez que mainVC implémente la méthode present2 - vous pouvez vous retrouver avec de difficile-à-débogage des erreurs et des pannes. Les délégués de vous aider à éviter cela.
  • une fois VC1 est rejetée, il n'est pas vraiment là pour exécuter l'achèvement de bloc... ou est-il? Ne auto.presentingViewController signifie plus rien? Vous ne savez pas (ni moi)... avec un délégué, vous n'avez pas cette incertitude.
  • Lorsque j'essaie d'exécuter cette méthode, il se contente d'être avec aucun message d'avertissement ou d'erreur.

Donc, s'il vous plaît... prenez le temps d'apprendre délégation!

update2

Dans votre commentaire vous avez réussi à le faire fonctionner en utilisant ce dans VC2 de rejeter gestionnaire de bouton:

 [self.view.window.rootViewController dismissViewControllerAnimated:YES completion:nil]; 

C'est certainement beaucoup plus simple, mais il vous laisse avec un certain nombre de questions.

Le couplage
Vous êtes le câblage de votre viewController structure d'ensemble. Par exemple, si vous insérez un nouveau viewController avant mainViewController, votre comportement, pause (vous accédez à l'avant). En VC1 vous avez également dû #import VC2. Donc vous avez tout à fait beaucoup d'inter-dépendances, qui rompt POO/MVC objectifs.

À l'aide de délégués, ni VC1, ni VC2 besoin de savoir quelque chose au sujet de mainVC ou c'est antecedants donc nous garder de tout faiblement couplées et modulaire.

De mémoire
VC1 n'a pas disparu, vous détenez toujours deux pointeurs:

  • mainVC de l' presentedViewController de la propriété
  • VC2 l' presentingViewController de la propriété

Vous pouvez tester cela en vous connectant, et aussi tout simplement en faisant ce de VC2

     [self dismissViewControllerAnimated:YES completion:nil]; 

Il fonctionne toujours, toujours vous ramène à VC1.

Cela me semble être une fuite de mémoire.

L'indice est dans l'avertissement que vous voyez ici:

[self presentViewController:vc2 animated:YES completion:nil];
[self dismissViewControllerAnimated:YES completion:nil];
 // Attempt to dismiss from view controller <VC1: 0x715e460>
 // while a presentation or dismiss is in progress!

La logique s'effondre, comme vous essayez de rejeter la présentation de VC qui VC2 est présenté VC. Le deuxième message n'est pas réellement exécuté - et peut-être certaines choses qui se passe, mais vous êtes toujours à gauche avec deux pointeurs vers un objet que vous pensiez de s'en débarrasser. (edit - j'ai vérifié et il n'est pas si mauvais, les deux objets ne disparaissent lorsque vous êtes de retour à mainVC)

C'est plutôt longue haleine façon de dire: - s'il vous plaît, utiliser des délégués. Si cela peut aider, j'ai fait une autre brève description du modèle ici:
Est le passage d'un contrôleur dans un construtor toujours une mauvaise pratique?

mise à jour 3
Si vous voulez vraiment éviter les délégués, ce pourrait être le meilleur moyen de sortir:

En VC1:

  [self presentViewController:VC2
                   animated:YES
                 completion:nil];

Mais ne pas rejeter quoi que ce soit... comme nous l'avons constaté, il n'a pas vraiment lieu de toute façon.

Dans VC2:

[self.presentingViewController.presentingViewController 
    dismissViewControllerAnimated:YES
                       completion:nil];

Comme nous (savoir) nous n'avons pas rejeté VC1, nous pouvons atteindre à travers elle à MainVC. MainVC rejette VC1. Parce que VC1 a disparu, il est présenté VC2 va avec, alors vous êtes de retour à MainVC dans un état propre.

C'est toujours très couplé, comme VC1 besoin de savoir à propos de VC2, et VC2 besoin de savoir qui il était arrivé à la via MainVC->VC1, mais c'est le meilleur que vous allez obtenir sans un peu de délégation explicite.

10voto

Radu Simionescu Points 687

Je pense que vous avez mal compris certains concepts de base sur iOS modal vue des contrôleurs. Lorsque vous fermez le VC1, le tout présenté de vue des contrôleurs par VC1 sont rejeté. Apple l'intention de vue modal contrôleurs de débit dans un empilées manière - dans votre cas, VC2 est présenté par VC1. Vous êtes rejetant VC1 dès que vous présentez VC2 de VC1 c'est donc un désordre total. Pour atteindre ce que vous voulez, buttonPressedFromVC1 devrait avoir le mainVC présent VC2 immédiatement après VC1 rejette lui-même. Et je pense que cela peut être réalisé sans les délégués. Quelque chose le long des lignes de:

UIViewController presentingVC = [self presentingViewController];
[self dismissViewControllerAnimated:YES completion:
 ^{
    [presentingVC presentViewController:vc2 animated:YES completion:nil];
 }];

Notez que l'auto.presentingViewController est stocké dans une autre variable, car après vc1 rejette lui-même, vous ne devriez pas faire référence à elle.

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