despedirModalViewController Y devolver datos

84

Tengo dos controladores de vista, firstViewController y secondViewController . Estoy usando este código para cambiar a mi secondViewController (también le estoy pasando una cadena):

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

Luego uso este código en secondViewController para volver al firstViewController:

[self dismissModalViewControllerAnimated:YES];

Todo esto funciona bien. Mi pregunta es, ¿cómo pasaría datos al firstViewController? Me gustaría pasar una cadena diferente al firstViewController desde el secondViewController.

Andrew Davis
fuente

Respuestas:

142

Necesita usar protocolos delegados ... A continuación, le indicamos cómo hacerlo:

Declare un protocolo en el archivo de encabezado de su secondViewController. Debe tener un aspecto como este:

#import <UIKit/UIKit.h>

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


@interface SecondViewController : UIViewController
{
    id myDelegate;  
}

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

No olvide sintetizar myDelegate en su archivo de implementación (SecondViewController.m):

@synthesize myDelegate;

En el archivo de encabezado de su FirstViewController, suscríbase al protocolo SecondDelegate haciendo esto:

#import "SecondViewController.h"

@interface FirstViewController:UIViewController <SecondDelegate>

Ahora, cuando crea una instancia de SecondViewController en FirstViewController, debe hacer lo siguiente:

// 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, en el archivo de implementación para su primer controlador de vista (FirstViewController.m) implemente el método de SecondDelegate para secondViewControllerDismissed:

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

Ahora, cuando esté a punto de descartar el segundo controlador de vista, desea invocar el método implementado en el primer controlador de vista. Esta parte es simple. Todo lo que hace es, en su segundo controlador de vista, agregar algo de código antes del código de descarte:

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

Los protocolos de delegado son EXTREMADAMENTE, EXTREMADAMENTE, EXTREMADAMENTE útiles. Te vendría bien familiarizarte con ellos :)

NSNotifications es otra forma de hacer esto, pero como mejor práctica, prefiero usarlo cuando quiero comunicarme a través de múltiples viewControllers u objetos. Aquí hay una respuesta que publiqué anteriormente si tiene curiosidad sobre el uso de NSNotifications: Activación de eventos a través de múltiples controladores de vista desde un hilo en la aplicación

EDITAR:

Si desea pasar varios argumentos, el código antes de descartar se ve así:

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

Esto significa que la implementación del método SecondDelegate dentro de su firstViewController ahora se verá así:

- (void) secondViewControllerDismissed:(NSString*)stringForFirst argument2:(NSObject*)inObject1 argument3:(NSObject*)inObject2
{
    NSString thisIsTheDesiredString = stringForFirst;
    NSObject desiredObject1 = inObject1;
    //....and so on
}
Sid
fuente
Según la Guía de programación del controlador de vista de Apple para iOS, el controlador de vista secundario debe descartarse en el controlador de vista de presentación, no en el presentado.
Michael
Parece que no ha configurado el delegado de UITableView. ¿Podrías publicar esto como una pregunta, con el código que tienes y volver con un círculo? Yo podría ser capaz de ayudarte.
Sid
1
@Michael La documentación dice que la llamada de despedida en self reenvía la llamada al controlador de vista de presentación. Además, llamar a sí mismo es más limpio, ya que de esa manera no necesita preocuparse por cambiar entre PresentationViewController y parentViewController según la versión de iOS a la que se dirige (5 o antes).
Sid
1
@Resty estoy de acuerdo; Los bloques son increíblemente útiles. Estaba considerando cambiar esta respuesta para admitir bloques en algún momento. Sin embargo, en este caso, dejé visible la respuesta del delegado por ahora porque nos da un poco más de libertad para manipular objetos que podrían pasar al modal. Soy vago y actualizaré esta respuesta para usar bloques pronto :)
Sid
1
@sid gracias hermano, funciona para mí, pero necesitas modificarlo un poco. como muchas cosas cambiaron. Por favor, editarlo
ChenSmile
40

Podría estar fuera de lugar aquí, pero estoy empezando a preferir la sintaxis de bloque al enfoque muy detallado de delegado / protocolo. Si crea vc2 desde vc1, tenga una propiedad en vc2 que pueda establecer desde vc1 que es un bloque

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

Luego, cuando ocurra algo en vc2 de lo que quiera informar a vc1, ¡simplemente ejecute el bloque que definió en vc1!

self.somethingHappenedInVC2(@"Hello!");

Esto le permite enviar datos desde vc2 a vc1. Como magia. En mi opinión, esto es mucho más fácil / más limpio que los protocolos. Los bloques son increíbles y deben aceptarse tanto como sea posible.

EDITAR - Ejemplo mejorado

Digamos que tenemos un mainVC que queremos presentar un modalVC además de temporalmente para obtener alguna entrada de un usuario. Para presentar ese modalVC desde mainVC, necesitamos alloc / init dentro de mainVC. Cosas bastante básicas. Bueno, cuando creamos este objeto modalVC, también podemos establecer una propiedad de bloque que nos permita comunicarnos fácilmente entre ambos objetos vc. Así que tomemos el ejemplo anterior y coloquemos la siguiente propiedad en el archivo .h de modalVC:

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

Luego, en nuestro mainVC, después de haber asignado / iniciado un nuevo objeto modalVC, establece la propiedad de bloque de modalVC de esta manera:

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

Así que solo estamos configurando la propiedad del bloque y definiendo lo que sucede cuando se ejecuta ese bloque.

Finalmente, en nuestro modalVC, podríamos tener un tableViewController respaldado por una matriz de cadenas de datos. Una vez que se realiza una selección de fila, podríamos hacer algo como esto:

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

Y, por supuesto, cada vez que seleccionamos una fila en modalVC, obtendremos una salida de consola de nuestra línea NSLog en mainVC. ¡Espero que ayude!

Lizza
fuente
1
¿Debería funcionar esto cuando se usan guiones gráficos? Ahora mismo no me está funcionando. Simplemente se cierra con un error lldb. La diferencia principal. Puedo ver que la asignación de vc ahora es un flujo de guión gráfico instanciado. EDITAR Y estaba presentando antes de crear el bloque. Fijo.
malaki1974
2
Estoy de acuerdo contigo :) Publiqué mi respuesta hace bastante tiempo. Ahora, cambio entre el uso de bloques / protocolos según el uso. Dado que este hilo todavía está bastante activo hasta el día de hoy, debería, en algún momento, editar mi respuesta para incluir bloques.
Sid
1
Esta respuesta debe aceptarse ya que brinda la solución más intuitiva.
Sukitha Udugamasooriya
2
De las dos respuestas adecuadas, ¡esta es la mejor!
kygcoleman
1
Este es, con mucho, mi método preferido. Rápidamente, esto se logra con cierres. Mucho mejor que los delegados y las notificaciones porque no es necesario especificar protocolos o estas constantes de notificación "feas". Si hace que el nombre de la variable en el vc presentado que contiene el cierre sea agradable, puede ser un código muy intuitivo, por ejemplo. Vc.didCancel, vc.didFinish ... Puede configurarlos en prepareForSegue f el vc que lo presenta (si está usando segues).
HixField
4

mmm, busque el centro de notificaciones y envíe información en una notificación. Aquí las manzanas lo aceptan : tomo este enfoque personalmente a menos que alguien tenga alguna otra sugerencia

theiOSDude
fuente
El enlace en realidad lo complica demasiado, todo lo que necesita es un observador (primer controlador de vista) y enviar la notificación desde el segundo. Puede asignar selectores a una notificación y obtener la información enviada a través de la notificación también.
theiOSDude
2

Defina un protocolo de delegado en el segundo controlador de vista y haga que el primero sea el delegado del segundo.

cschwarz
fuente