ignorerModalViewController ET renvoyer les données

84

J'ai deux contrôleurs de vue, firstViewController et secondViewController . J'utilise ce code pour passer à mon secondViewController (je lui passe également une chaîne):

secondViewController *second = [[secondViewController alloc] initWithNibName:nil bundle:nil];

second.myString = @"This text is passed from firstViewController!";

second.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;

[self presentModalViewController:second animated:YES];

[second release];

J'utilise ensuite ce code dans secondViewController pour revenir au firstViewController:

[self dismissModalViewControllerAnimated:YES];

Tout cela fonctionne très bien. Ma question est la suivante: comment passer des données au firstViewController? Je voudrais passer une chaîne différente dans le firstViewController du secondViewController.

Andrew Davis
la source

Réponses:

142

Vous devez utiliser des protocoles de délégation ... Voici comment procéder:

Déclarez un protocole dans le fichier d'en-tête de votre secondViewController. Ça devrait ressembler à ça:

#import <UIKit/UIKit.h>

@protocol SecondDelegate <NSObject>
-(void)secondViewControllerDismissed:(NSString *)stringForFirst
@end


@interface SecondViewController : UIViewController
{
    id myDelegate;  
}

@property (nonatomic, assign) id<SecondDelegate>    myDelegate;

N'oubliez pas de synthétiser le myDelegate dans votre fichier d'implémentation (SecondViewController.m):

@synthesize myDelegate;

Dans le fichier d'en-tête de votre FirstViewController, abonnez-vous au protocole SecondDelegate en procédant comme suit:

#import "SecondViewController.h"

@interface FirstViewController:UIViewController <SecondDelegate>

Maintenant, lorsque vous instanciez SecondViewController dans FirstViewController, vous devez effectuer les opérations suivantes:

// If you're using a view controller built with Interface Builder.
SecondViewController *second = [[SecondViewController alloc] initWithNibName:"SecondViewController" bundle:[NSBundle mainBundle]];
// If you're using a view controller built programmatically.
SecondViewController *second = [SecondViewController new]; // Convenience initializer that uses alloc] init]
second.myString = @"This text is passed from firstViewController!";
second.myDelegate = self;
second.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:second animated:YES];
[second release];

Enfin, dans le fichier d'implémentation de votre premier contrôleur de vue (FirstViewController.m), implémentez la méthode de SecondDelegate pour secondViewControllerDismissed:

- (void)secondViewControllerDismissed:(NSString *)stringForFirst
{
    NSString *thisIsTheDesiredString = stringForFirst; //And there you have it.....
}

Maintenant, lorsque vous êtes sur le point de fermer le deuxième contrôleur de vue, vous souhaitez appeler la méthode implémentée dans le premier contrôleur de vue. Cette partie est simple. Tout ce que vous faites est, dans votre deuxième contrôleur de vue, d'ajouter du code avant le code de rejet:

if([self.myDelegate respondsToSelector:@selector(secondViewControllerDismissed:)])
{
    [self.myDelegate secondViewControllerDismissed:@"THIS IS THE STRING TO SEND!!!"];
}
[self dismissModalViewControllerAnimated:YES];

Les protocoles délégués sont EXTRÊMEMENT, EXTRÊMEMENT, EXTRÊMEMENT utiles. Cela vous ferait du bien de vous familiariser avec eux :)

NSNotifications est une autre façon de faire cela, mais comme meilleure pratique, je préfère l'utiliser lorsque je souhaite communiquer sur plusieurs viewControllers ou objets. Voici une réponse que j'ai publiée plus tôt si vous êtes curieux d'utiliser NSNotifications: déclencher des événements sur plusieurs contrôleurs de vue à partir d'un fil de discussion dans l'appdelegate

ÉDITER:

Si vous souhaitez passer plusieurs arguments, le code avant de rejeter ressemble à ceci:

if([self.myDelegate respondsToSelector:@selector(secondViewControllerDismissed:argument2:argument3:)])
{
    [self.myDelegate secondViewControllerDismissed:@"THIS IS THE STRING TO SEND!!!" argument2:someObject argument3:anotherObject];
}
[self dismissModalViewControllerAnimated:YES];

Cela signifie que votre implémentation de méthode SecondDelegate dans votre firstViewController ressemblera maintenant à:

- (void) secondViewControllerDismissed:(NSString*)stringForFirst argument2:(NSObject*)inObject1 argument3:(NSObject*)inObject2
{
    NSString thisIsTheDesiredString = stringForFirst;
    NSObject desiredObject1 = inObject1;
    //....and so on
}
Sid
la source
Selon le guide de programmation du contrôleur d' affichage d'Apple pour iOS, le secondViewController doit être rejeté dans le contrôleur d'affichage de présentation, et non dans celui présenté.
Michael
On dirait que vous n'avez pas défini le délégué de UITableView. Pourriez-vous poster ceci sous forme de question, avec le code que vous avez et revenir en arrière? Je pourrais peut-être vous aider.
Sid
1
@Michael La documentation indique que l'appel de la fonction de renvoi sur soi transfère l'appel au contrôleur de vue de présentation. De plus, appeler soi-même est plus propre, car vous n'avez pas à vous soucier de basculer entre PresentViewController et parentViewController en fonction de la version iOS que vous ciblez (5 ou avant).
Sid
1
@Resty Je suis d'accord; les blocs sont incroyablement utiles. J'envisageais de changer cette réponse pour prendre en charge les blocs à un moment donné. Cependant dans ce cas, j'ai laissé la réponse du délégué visible pour l'instant car elle nous donne un peu plus de liberté pour manipuler les objets qui pourraient être passés dans le modal. Je suis juste paresseux et je mettrai à jour cette réponse pour utiliser des blocs bientôt :)
Sid
1
@sid merci bro cela fonctionne pour moi mais vous devez légèrement modifier. autant de choses ont changé. veuillez le modifier
ChenSmile
40

Je pourrais être hors de propos ici, mais je commence à préférer de beaucoup la syntaxe de bloc à l'approche très verbeuse délégué / protocole. Si vous créez vc2 à partir de vc1, ayez une propriété sur vc2 que vous pouvez définir à partir de vc1 qui est un bloc!

@property (nonatomic, copy) void (^somethingHappenedInVC2)(NSString *response);

Ensuite, quand quelque chose se passe dans vc2 dont vous voulez parler à vc1, exécutez simplement le bloc que vous avez défini dans vc1!

self.somethingHappenedInVC2(@"Hello!");

Cela vous permet de renvoyer les données de vc2 vers vc1. Tout comme la magie. IMO, c'est beaucoup plus facile / plus propre que les protocoles. Les blocs sont géniaux et doivent être adoptés autant que possible.

EDIT - Exemple amélioré

Disons que nous avons un mainVC sur lequel nous voulons présenter temporairement un modalVC pour obtenir une entrée d'un utilisateur. Afin de présenter ce modalVC de mainVC, nous devons l'allouer / l'initier à l'intérieur de mainVC. Des trucs assez basiques. Eh bien, lorsque nous créons cet objet modalVC, nous pouvons également définir une propriété de bloc qui nous permet de communiquer facilement entre les deux objets vc. Prenons donc l'exemple ci-dessus et mettons la propriété follwing dans le fichier .h de modalVC:

 @property (nonatomic, copy) void (^somethingHappenedInModalVC)(NSString *response);  

Ensuite, dans notre mainVC, après avoir alloué / initié un nouvel objet modalVC, vous définissez la propriété block de modalVC comme ceci:

ModalVC *modalVC = [[ModalVC alloc] init];
modalVC.somethingHappenedInModalVC = ^(NSString *response) {
     NSLog(@"Something was selected in the modalVC, and this is what it was:%@", response);
}

Nous définissons donc simplement la propriété du bloc et définissons ce qui se passe lorsque ce bloc est exécuté.

Enfin, dans notre modalVC, nous pourrions avoir un tableViewController qui est soutenu par un tableau dataSource de chaînes. Une fois qu'une sélection de ligne est faite, nous pourrions faire quelque chose comme ceci:

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
      NSString *selectedString = self.dataSource[indexPath.row];
      self.somethingHappenedInModalVC(selectedString);
 }

Et bien sûr, chaque fois que nous sélectionnons une ligne dans modalVC, nous allons récupérer une sortie console de notre ligne NSLog dans mainVC. J'espère que ça t'as aidé!

Lizza
la source
1
Cela devrait-il toujours fonctionner lors de l'utilisation de storyboards? En ce moment, cela ne fonctionne pas pour moi. Quitte simplement avec une erreur lldb. Le principal diff. Je peux voir que l'allocation du vc est maintenant un flux de storyboard instancié. EDIT Et je faisais une présentation avant de créer le bloc. Fixé.
malaki1974
2
Je suis d'accord avec vous :) J'ai posté ma réponse il y a pas mal de temps. Maintenant, je bascule entre l'utilisation de blocs / protocoles en fonction de l'utilisation. Étant donné que ce fil est encore assez actif à ce jour, je devrais, à un moment donné, modifier ma réponse pour inclure des blocs.
Sid
1
Cette réponse doit être acceptée car elle apporte la solution la plus intuitive.
Sukitha Udugamasooriya
2
Sur les deux réponses appropriées, c'est la meilleure!
kygcoleman
1
C'est de loin ma méthode préférée. En un rien de temps, cela se fait ensuite avec des fermetures. Bien mieux que les délégués et les notifications, car vous n'avez pas besoin de spécifier de protocoles ou ces constantes de notification "laides". Si vous rendez le nom de la variable dans le vc présenté qui contient la fermeture agréable, cela peut être un code très intuitif, par exemple. Vc.didCancel, vc.didFinish ... Vous pouvez les définir dans le prepareForSegue du vc qui le présente (si vous utilisez des segues).
HixField
4

hmm, recherchez le centre de notification et renvoyez les informations dans une notification. voici que les pommes le prennent - je prends cette approche personnellement à moins que quelqu'un n'ait d'autres suggestions

theiOSDude
la source
Le lien le complique en fait, tout ce dont vous avez besoin est un observateur (premier contrôleur de vue) et envoyer le notif du second. Vous pouvez affecter des sélecteurs à une notification et obtenir les informations renvoyées via la notification également.
theiOSDude
2

Définissez un protocole de délégué dans le second contrôleur de vue et faites du premier le délégué du second.

cschwarz
la source