dispensarModalViewController E transmitir dados de volta

84

Eu tenho dois controladores de visualização, firstViewController e secondViewController . Estou usando este código para alternar para meu secondViewController (também estou passando uma string para ele):

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];

Em seguida, uso este código em secondViewController para voltar para o firstViewController:

[self dismissModalViewControllerAnimated:YES];

Tudo isso funciona bem. Minha pergunta é: como eu passaria dados para o firstViewController? Eu gostaria de passar uma string diferente para o firstViewController do secondViewController.

Andrew Davis
fonte

Respostas:

142

Você precisa usar protocolos delegados ... Veja como fazer isso:

Declare um protocolo no arquivo de cabeçalho do secondViewController. Deve ser assim:

#import <UIKit/UIKit.h>

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


@interface SecondViewController : UIViewController
{
    id myDelegate;  
}

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

Não se esqueça de sintetizar o myDelegate em seu arquivo de implementação (SecondViewController.m):

@synthesize myDelegate;

No arquivo de cabeçalho do seu FirstViewController, inscreva-se no protocolo SecondDelegate fazendo o seguinte:

#import "SecondViewController.h"

@interface FirstViewController:UIViewController <SecondDelegate>

Agora, ao instanciar SecondViewController em FirstViewController, você deve fazer o seguinte:

// 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];

Por último, no arquivo de implementação do seu primeiro controlador de visualização (FirstViewController.m), implemente o método SecondDelegate para secondViewControllerDismissed:

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

Agora, quando você está prestes a dispensar o segundo controlador de visualização, deseja invocar o método implementado no primeiro controlador de visualização. Esta parte é simples. Tudo o que você faz é, em seu segundo controlador de visualização, adicionar algum código antes de dispensar o código:

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

Os protocolos de delegação são EXTREMAMENTE, EXTREMAMENTE, EXTREMAMENTE úteis. Seria bom se familiarizar com eles :)

NSNotifications são outra maneira de fazer isso, mas como prática recomendada, prefiro usá-lo quando quero me comunicar entre vários viewControllers ou objetos. Aqui está uma resposta que postei anteriormente se você estiver curioso sobre como usar NSNotifications: Eventos de disparo em vários controladores de visualização de um thread no appdelegate

EDITAR:

Se você quiser passar vários argumentos, o código antes de dispensar terá a seguinte aparência:

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

Isso significa que a implementação do método SecondDelegate dentro do firstViewController será semelhante a:

- (void) secondViewControllerDismissed:(NSString*)stringForFirst argument2:(NSObject*)inObject1 argument3:(NSObject*)inObject2
{
    NSString thisIsTheDesiredString = stringForFirst;
    NSObject desiredObject1 = inObject1;
    //....and so on
}
Sid
fonte
De acordo com o View Controller Programming Guide for iOS da Apple, o secondViewController deve ser descartado no view controller de apresentação, não no apresentado.
Michael
Parece que você não definiu o delegado do UITableView. Você poderia postar isso como uma pergunta, com o código que você tem e circule de volta? Eu posso te ajudar.
Sid,
1
@Michael A documentação diz que chamar dispense em self encaminha a chamada para o controlador de visualização de apresentação. Além disso, chamar a si mesmo é mais limpo, pois você não precisa se preocupar em alternar entre presentViewController e parentViewController, dependendo da versão do iOS que você deseja (5 ou anterior).
Sid
1
@Resty eu concordo; blocos são incrivelmente úteis. Eu estava pensando em mudar essa resposta para blocos de suporte em algum momento. No entanto, neste caso, deixei a resposta do delegado visível por enquanto, porque nos dá um pouco mais de liberdade para manipular objetos que podem ser passados ​​para o modal. Estou com preguiça e irei atualizar esta resposta para usar blocos em breve :)
Sid
1
@sid obrigado mano funciona para mim, mas você precisa modificar um pouco. tantas coisas mudaram. edite-o
ChenSmile
40

Eu poderia estar muito fora do lugar aqui, mas estou começando a preferir muito mais a sintaxe de bloco à abordagem muito prolixa de delegado / protocolo. Se você fizer vc2 a partir de vc1, tenha uma propriedade em vc2 que você pode definir a partir de vc1 que é um bloco!

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

Então, quando algo acontecer em vc2 que você deseja informar a vc1, apenas execute o bloco que você definiu em vc1!

self.somethingHappenedInVC2(@"Hello!");

Isso permite que você envie dados de vc2 de volta para vc1. Como mágica. IMO, isso é muito mais fácil / mais limpo do que protocolos. Os blocos são fantásticos e precisam ser adotados o máximo possível.

EDIT - exemplo aprimorado

Digamos que temos um mainVC ao qual desejamos apresentar um modalVC temporariamente para obter alguma entrada de um usuário. Para apresentar esse VC modal de mainVC, precisamos alocá-lo / iniciá-lo dentro de mainVC. Coisas bem básicas. Bem, quando fazemos este objeto VC modal, também podemos definir uma propriedade de bloco nele que nos permite comunicar facilmente entre os dois objetos vc. Então, vamos pegar o exemplo acima e colocar a propriedade a seguir no arquivo .h do modalVC:

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

Então, em nosso mainVC, depois de alocar / inicializar um novo objeto modalVC, você define a propriedade block de modalVC assim:

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

Portanto, estamos apenas configurando a propriedade do bloco e definindo o que acontece quando esse bloco é executado.

Finalmente, em nosso modalVC, poderíamos ter um tableViewController que é apoiado por um array dataSource de strings. Depois de fazer uma seleção de linha, poderíamos fazer algo assim:

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

E, claro, cada vez que selecionarmos uma linha em modalVC, obteremos uma saída de console de nossa linha NSLog de volta em mainVC. Espero que ajude!

Lizza
fonte
1
Isso ainda deve funcionar ao usar storyboards? No momento, não está funcionando para mim. Apenas fecha com um erro de lldb. O diferencial principal. Eu posso ver que a alocação do vc agora é um fluxo de storyboard instanciado. EDIT E eu estava apresentando antes de criar o bloco. Fixo.
malaki1974
2
Eu concordo com você :) Eu postei minha resposta há algum tempo. Agora, eu alterno entre o uso de blocos / protocolos dependendo do uso. Visto que este tópico ainda está muito ativo até hoje, devo, em algum momento, editar minha resposta para incluir blocos.
Sid
1
Essa resposta precisa ser aceita, pois traz a solução mais intuitiva.
Sukitha Udugamasooriya
2
Das duas respostas adequadas, esta é a melhor!
Kygcoleman
1
Este é de longe o meu método preferido. Com rapidez, isso é feito com fechamentos. Muito melhor que delegados e notificações, porque você não precisa especificar protocolos ou essas constantes de notificação "feias". Se você fizer o nome da variável no vc apresentado que contém o fechamento bonito, pode ser um código muito intuitivo, por exemplo. Vc.didCancel, vc.didFinish ... Você pode configurá-los no prepareForSegue do vc que o apresenta (se estiver usando segues).
HixField
4

hmm, procure a central de notificações e devolva informações em uma notificação. aqui está as maçãs - eu considero essa abordagem pessoalmente, a menos que alguém tenha alguma outra sugestão

theiOSDude
fonte
Na verdade, o link complica demais, tudo que você precisa é um observador (primeiro View Controller) e enviar o notif do segundo. Você pode atribuir seletores a uma notificação e também obter as informações enviadas de volta por meio da notificação.
theiOSDude
2

Defina um protocolo de delegado no segundo controlador de visão e torne o primeiro o delegado do segundo.

cschwarz
fonte