Pasar datos entre los controladores de vista

Soy nuevo en iOS y Objective-C y en todo el paradigma MVC y estoy atrapado con lo siguiente:

Tengo una vista que actúa como un formulario de entrada de datos y quiero darle al usuario la opción de seleccionar múltiples productos. Los productos se enumeran en otra vista con un UITableViewController y he habilitado varias selecciones.

Mi pregunta es, ¿cómo transfiero los datos de una vista a otra? UITableView las selecciones en el UITableView en una matriz, pero ¿cómo puedo pasar eso a la vista de formulario de entrada de datos anterior para que pueda guardarse junto con los demás datos en Core Data en el momento del envío del formulario?

He navegado y he visto a algunas personas declarar una matriz en el delegado de la aplicación. Leí algo sobre Singletons pero no entiendo cuáles son estos y leo algo sobre cómo crear un modelo de datos.

¿Cuál sería la forma correcta de realizar esto y cómo lo haría?

Esta pregunta parece ser muy popular aquí en stackoverflow, así que pensé en intentar dar una mejor respuesta para ayudar a las personas que comienzan en el mundo de iOS como yo.

Espero que esta respuesta sea lo suficientemente clara como para que la gente entienda y que no me haya perdido nada.

Pasar datos adelante

Pasar datos hacia adelante a un controlador de vista desde otro controlador de vista. Utilizaría este método si quisiera pasar un objeto / valor de un controlador de vista a otro controlador de vista que puede estar presionando en una stack de navegación.

Para este ejemplo, tendremos ViewControllerA y ViewControllerB

Para pasar un valor BOOL de ViewControllerA a ViewControllerB , haríamos lo siguiente.

  1. en ViewControllerB.h crea una propiedad para BOOL

     @property (nonatomic, assign) BOOL isSomethingEnabled; 
  2. en ViewControllerA necesitas contarle acerca de ViewControllerB así que usa un

     #import "ViewControllerB.h" 

    Entonces, donde quieres cargar la vista, por ej. didSelectRowAtIndex o alguna IBAction necesita establecer la propiedad en ViewControllerB antes de insertarla en la stack de navegación.

     ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil]; viewControllerB.isSomethingEnabled = YES; [self pushViewController:viewControllerB animated:YES]; 

    Esto establecerá isSomethingEnabled en ViewControllerB a BOOL valor YES .

Pasar datos adelante usando Segues

Si está utilizando Storyboards, lo más probable es que esté utilizando segues y necesitará este procedimiento para pasar los datos hacia adelante. Esto es similar al anterior, pero en lugar de pasar los datos antes de presionar el controlador de vista, utiliza un método llamado

 -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 

Entonces, para pasar un BOOL de ViewControllerA a ViewControllerB , haríamos lo siguiente:

  1. en ViewControllerB.h crea una propiedad para BOOL

     @property (nonatomic, assign) BOOL isSomethingEnabled; 
  2. en ViewControllerA necesitas contarle acerca de ViewControllerB así que usa un

     #import "ViewControllerB.h" 
  3. Cree una transición de ViewControllerA a ViewControllerB en el guión gráfico y "showDetailSegue" un identificador, en este ejemplo lo llamaremos "showDetailSegue"

  4. A continuación, necesitamos agregar el método a ViewControllerA que se llama cuando se realiza cualquier segue, debido a esto tenemos que detectar qué segue fue llamado y luego hacer algo. En nuestro ejemplo, buscaremos "showDetailSegue" y si eso se lleva a cabo pasaremos nuestro valor BOOL a ViewControllerB

     -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ if([segue.identifier isEqualToString:@"showDetailSegue"]){ ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController; controller.isSomethingEnabled = YES; } } 

    Si tiene sus vistas incrustadas en un controlador de navegación, debe cambiar ligeramente el método anterior a la siguiente

     -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ if([segue.identifier isEqualToString:@"showDetailSegue"]){ UINavigationController *navController = (UINavigationController *)segue.destinationViewController; ViewControllerB *controller = (ViewControllerB *)navController.topViewController; controller.isSomethingEnabled = YES; } } 

    Esto establecerá isSomethingEnabled en ViewControllerB a BOOL valor YES .

Pasando datos de vuelta

Para pasar datos de ViewControllerB a ViewControllerA necesita usar Protocolos y Delegados o Bloques , este último puede usarse como un mecanismo débilmente acoplado para callbacks.

Para hacer esto, haremos de ViewControllerA un delegado de ViewControllerB . Esto permite a ViewControllerB enviar un mensaje a ViewControllerA permitiéndonos enviar datos de vuelta.

Para que ViewControllerA sea ​​delegado de ViewControllerB debe cumplir con el protocolo ViewControllerB que debemos especificar. Esto le dice a ViewControllerA qué métodos debe implementar.

  1. En ViewControllerB.h , debajo de la @interface #import , pero arriba @interface , especifica el protocolo.

     @class ViewControllerB; @protocol ViewControllerBDelegate  - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item; @end 
  2. ViewControllerB.h aún en ViewControllerB.h , necesita configurar una propiedad de delegate y sintetizar en ViewControllerB.m

     @property (nonatomic, weak) id  delegate; 
  3. En ViewControllerB llamamos un mensaje en el delegate cuando mostramos el controlador de vista.

     NSString *itemToPassBack = @"Pass this value back to ViewControllerA"; [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack]; 
  4. Eso es todo para ViewControllerB . Ahora en ViewControllerA.h , dile a ViewControllerA que importe ViewControllerB y que se ajuste a su protocolo.

     #import "ViewControllerB.h" @interface ViewControllerA : UIViewController  
  5. En ViewControllerA.m implementamos el siguiente método de nuestro protocolo

     - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item { NSLog(@"This was returned from ViewControllerB %@",item); } 
  6. Antes de viewControllerB a la stack de navegación, debemos decirle a ViewControllerB que ViewControllerA es su delegado, de lo contrario, obtendremos un error.

     ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil]; viewControllerB.delegate = self [[self navigationController] pushViewController:viewControllerB animated:YES]; 

Referencias

  • Usar la delegación para comunicarse con otros controladores de vista en la guía de progtwigción de View Controller
  • Delegar patrón

Rápido

Hay toneladas y toneladas de explicaciones aquí y en todo StackOverflow, pero si eres un principiante y solo intentas hacer funcionar algo básico, prueba mirar este tutorial de YouTube (fue lo que finalmente me ayudó a entender cómo hacerlo).

  • Tutorial de YouTube: Cómo enviar datos a través de segue (swift)

Pasar los datos a la siguiente Controladora de Vista

El siguiente es un ejemplo basado en el video. La idea es pasar una cadena del campo de texto en el Controlador de primera vista a la etiqueta en el Controlador de segunda vista.

enter image description here

Cree el diseño del guión gráfico en Interface Builder. Para hacer la transición, simplemente haga clic en el botón y arrastre hacia el Controlador de segunda vista.

Primer controlador de vista

El código para el First View Controller es

 import UIKit class FirstViewController: UIViewController { @IBOutlet weak var textField: UITextField! // This function is called before the segue override func prepare(for segue: UIStoryboardSegue, sender: Any?) { // get a reference to the second view controller let secondViewController = segue.destination as! SecondViewController // set a variable in the second view controller with the String to pass secondViewController.receivedString = textField.text! } } 

Controlador de segunda vista

Y el código para el segundo controlador de vista es

 import UIKit class SecondViewController: UIViewController { @IBOutlet weak var label: UILabel! // This variable will hold the data being passed from the First View Controller var receivedString = "" override func viewDidLoad() { super.viewDidLoad() // Used the text from the First View Controller to set the label label.text = receivedString } } 

No lo olvides

  • UITextField las tomas de UITextField y UILabel .
  • Establezca el primer y segundo controlador de vista en los archivos Swift apropiados en IB.

Pasar datos de vuelta al controlador de vista anterior

Para pasar datos desde el segundo controlador de vista al primer controlador de vista, usa un protocolo y un delegado . Este video es una caminata muy clara de ese proceso:

  • Tutorial de YouTube: Tutorial básico de iOS Swift: Protocolos y delegates. Pero también lea esta publicación para asegurarse de no entrar en un ciclo de referencia fuerte.

El siguiente es un ejemplo basado en el video (con algunas modificaciones).

enter image description here

Cree el diseño del guión gráfico en Interface Builder. Nuevamente, para hacer la transición, simplemente controle arrastrar desde el botón hasta el controlador de segunda vista. Establezca el identificador de segue para showSecondViewController . Además, no se olvide de conectar los puntos de venta y las acciones usando los nombres en el siguiente código.

Primer controlador de vista

El código para el First View Controller es

 import UIKit class FirstViewController: UIViewController, DataEnteredDelegate { @IBOutlet weak var label: UILabel! override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "showSecondViewController" { let secondViewController = segue.destination as! SecondViewController secondViewController.delegate = self } } func userDidEnterInformation(info: String) { label.text = info } } 

Tenga en cuenta el uso de nuestro protocolo DataEnteredDelegate personalizado.

Controlador y protocolo de segunda vista

El código para el segundo controlador de vista es

 import UIKit // protocol used for sending data back protocol DataEnteredDelegate: class { func userDidEnterInformation(info: String) } class SecondViewController: UIViewController { // making this a weak variable so that it won't create a strong reference cycle weak var delegate: DataEnteredDelegate? = nil @IBOutlet weak var textField: UITextField! @IBAction func sendTextBackButton(sender: AnyObject) { // call this method on whichever class implements our delegate protocol delegate?.userDidEnterInformation(info: textField.text!) // go back to the previous view controller _ = self.navigationController?.popViewController(animated: true) } } 

Tenga en cuenta que el protocol está fuera de la clase View Controller.

Eso es. Al ejecutar la aplicación ahora, debería poder enviar datos desde el segundo controlador de vista al primero.

El M en MVC es para “Modelo” y en el paradigma MVC el rol de las clases modelo es administrar los datos de un progtwig. Un modelo es lo opuesto a una vista: una vista sabe cómo mostrar datos, pero no sabe nada sobre qué hacer con los datos, mientras que un modelo sabe todo sobre cómo trabajar con los datos, pero nada sobre cómo mostrarlos. Los modelos pueden ser complicados, pero no tienen que serlo; el modelo para su aplicación podría ser tan simple como una matriz de cadenas o diccionarios.

El papel de un controlador es mediar entre la vista y el modelo. Por lo tanto, necesitan una referencia a uno o más objetos de visualización y uno o más objetos de modelo. Digamos que su modelo es una matriz de diccionarios, con cada diccionario representando una fila en su tabla. La vista raíz de su aplicación muestra esa tabla, y puede ser responsable de cargar la matriz desde un archivo. Cuando el usuario decide agregar una nueva fila a la tabla, toca un botón y el controlador crea un nuevo diccionario (mutable) y lo agrega a la matriz. Para completar la fila, el controlador crea un controlador de vista detallada y le da el nuevo diccionario. El controlador de vista detallada rellena el diccionario y lo devuelve. El diccionario ya es parte del modelo, por lo que no es necesario que ocurra nada más.

Hay varias maneras en que los datos se pueden recibir en una clase diferente en iOS. Por ejemplo –

  1. Inicialización directa después de la asignación de otra clase.
  2. Delegación – para pasar datos de vuelta
  3. Notificación: para transmitir datos a múltiples clases a la vez
  4. Guardar en NSUserDefaults – para acceder más tarde
  5. Clases Singleton
  6. Bases de datos y otros mecanismos de almacenamiento como plist, etc.

Pero para el escenario simple de pasar un valor a una clase diferente cuya asignación se realiza en la clase actual, el método más común y preferido sería la configuración directa de los valores después de la asignación. Esto se hace de la siguiente manera:

Podemos entenderlo usando dos controladores: Controller1 y Controller2

Supongamos que en la clase Controller1 desea crear el objeto Controller2 y presionarlo con un valor String que se pasa. Esto se puede hacer así:

 - (void)pushToController2 { Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil]; [obj passValue:@"String"]; [self pushViewController:obj animated:YES]; } 

En la implementación de la clase Controller2 habrá esta función como-

 @interface Controller2 : NSObject @property (nonatomic , strong) NSString* stringPassed; @end @implementation Controller2 @synthesize stringPassed = _stringPassed; - (void) passValue:(NSString *)value { _stringPassed = value; //or self.stringPassed = value } @end 

También puede establecer directamente las propiedades de la clase Controller2 de la misma manera que esta:

 - (void)pushToController2 { Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil]; [obj setStringPassed:@"String"]; [self pushViewController:obj animated:YES]; } 

Para pasar múltiples valores puede usar los parámetros múltiples como:

 Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil]; [obj passValue:@“String1” andValues:objArray withDate:date]; 

O si necesita pasar más de 3 parámetros que están relacionados con una característica común, puede almacenar los valores en una clase Model y pasar ese modelObject a la siguiente clase

 ModelClass *modelObject = [[ModelClass alloc] init]; modelObject.property1 = _property1; modelObject.property2 = _property2; modelObject.property3 = _property3; Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil]; [obj passmodel: modelObject]; 

En fin, si quieres,

 1) set the private variables of the second class initialise the values by calling a custom function and passing the values. 2) setProperties do it by directlyInitialising it using the setter method. 3) pass more that 3-4 values related to each other in some manner , then create a model class and set values to its object and pass the object using any of the above process. 

Espero que esto ayude

Después de más investigaciones, parece que Protocolos y delegates es la forma correcta / preferida de Apple de hacer esto.

Terminé usando este ejemplo

Compartir datos entre los controladores de vista y otros objetos @ iPhone Dev SDK

Funcionó bien y me permitió pasar una cadena y una matriz hacia adelante y hacia atrás entre mis puntos de vista.

Gracias por toda tu ayuda

Encuentro la versión más simple y elegante con bloques pasantes. Vamos a nombrar controlador de vista que espera datos devueltos como “A” y devuelve controlador de vista como “B”. En este ejemplo, queremos obtener 2 valores: primero de Type1 y segundo de Type2.

Suponiendo que usemos Storyboard, el primer controlador establece el locking de callback, por ejemplo, durante la preparación de segue:

 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.destinationViewController isKindOfClass:[BViewController class]]) { BViewController *viewController = segue.destinationViewController; viewController.callback = ^(Type1 *value1, Type2 *value2) { // optionally, close B //[self.navigationController popViewControllerAnimated:YES]; // let's do some action after with returned values action1(value1); action2(value2); }; } } 

y el controlador de vista “B” debería declarar la propiedad de callback, BViewController.h:

 // it is important to use "copy" @property (copy) void(^callback)(Type1 *value1, Type2 *value2); 

Que en el archivo de implementación BViewController.m después de que tengamos los valores deseados para devolver nuestra callback debe llamarse:

 if (self.callback) self.callback(value1, value2); 

Una cosa para recordar es que el uso de bloque a menudo necesita administrar referencias fuertes y __weak como se explica aquí

Hay buena información en muchas de las respuestas dadas, pero ninguna aborda la pregunta completamente.

La pregunta pregunta sobre pasar información entre los controladores de vista. El ejemplo específico que se da pregunta acerca de la transmisión de información entre vistas, pero dado el carácter autodenominado de iOS, el póster original probablemente significó entre viewControllers, no entre vistas (sin la participación de ViewControllers). Parece que todas las respuestas se centran en dos controladores de vista, pero ¿qué ocurre si la aplicación evoluciona para necesitar la participación de más de dos controladores de visualización en el intercambio de información?

El póster original también preguntó sobre Singletons y el uso de AppDelegate . Estas preguntas deben ser respondidas.

Para ayudar a cualquier otra persona que vea esta pregunta, que quiera una respuesta completa, intentaré brindarla.

Escenarios de aplicación

En lugar de tener una discusión abstracta muy hipotética, ayuda tener aplicaciones concretas en mente. Para ayudar a definir una situación de controlador de dos vistas y una situación de controlador de más de dos vistas, voy a definir dos escenarios de aplicación concretos.

Escenario uno: máximo dos controladores de vista alguna vez necesitan compartir información. Ver diagtwig uno.

diagrama del problema original

Hay dos controladores de vista en la aplicación. Hay un ViewControllerA (Formulario de entrada de datos) y un Controlador de vista B (Lista de productos). Los elementos seleccionados en la lista de productos deben coincidir con los elementos que se muestran en el cuadro de texto en el formulario de entrada de datos. En este escenario, ViewControllerA y ViewControllerB deben comunicarse directamente entre sí y sin otros controladores de vista.

Escenario dos : más de dos controladores de vista necesitan compartir la misma información. Vea el diagtwig dos.

diagrama de aplicación de inventario

Hay cuatro controladores de vista en la aplicación. Es una aplicación basada en tabs para administrar el inventario de la casa. Tres controladores de vista presentan vistas filtradas de manera diferente de los mismos datos:

  • ViewControllerA – Artículos de lujo
  • ViewControllerB – Artículos no asegurados
  • ViewControllerC – Inventario completo de la casa
  • ViewControllerD – Agregar nuevo formulario de elemento

Cada vez que se crea o edita un elemento individual, también debe sincronizarse con los demás controladores de vista. Por ejemplo, si agregamos un bote en ViewControllerD, pero aún no está asegurado, entonces el bote debe aparecer cuando el usuario vaya a ViewControllerA (Artículos de lujo), y también a ViewControllerC (Inventario completo de la vivienda), pero no cuando el usuario vaya a ViewControllerB (elementos no asegurados). Debemos preocuparnos no solo de agregar elementos nuevos, sino también de eliminar elementos (que pueden permitirse desde cualquiera de los cuatro controladores de vista) o editar elementos existentes (que pueden permitirse desde el “Formulario de Agregar nuevo elemento”, reutilizando el mismo para editar).

Como todos los controladores de vista necesitan compartir los mismos datos, los cuatro controladores de vista deben permanecer sincronizados y, por lo tanto, debe haber algún tipo de comunicación con todos los demás controladores de vista, siempre que cualquier controlador de vista individual cambie los datos subyacentes. Debería ser bastante obvio que no queremos que cada controlador de vista se comunique directamente con cada controlador de vista en este escenario. En caso de que no sea obvio, considere si teníamos 20 controladores de vista diferentes (en lugar de solo 4). ¿Qué tan difícil y propenso a errores sería notificar a cada uno de los otros 19 controladores de vista cada vez que un controlador de vista realizara un cambio?

Las soluciones: delegates y el patrón de observador, y Singletons

En el escenario uno, tenemos varias soluciones viables, como otras respuestas han dado

  • segues
  • delegates
  • establecer propiedades en los controladores de vista directamente
  • NSUserDefaults (en realidad, una mala elección)

En el escenario dos, tenemos otras soluciones viables:

  • Patrón de observador
  • Singletons

Un singleton es una instancia de una clase, siendo esa instancia la única instancia existente durante su vida útil. Un singleton recibe su nombre del hecho de que es la única instancia. Normalmente, los desarrolladores que usan singleton tienen métodos de clases especiales para acceder a ellos.

 + (HouseholdInventoryManager*) sharedManager; { static dispatch_once_t onceQueue; static HouseholdInventoryManager* _sharedInstance; // dispatch_once is guaranteed to only be executed once in the // lifetime of the application dispatch_once(&onceQueue, ^{ _sharedInstance = [[self alloc] init]; }); return _sharedInstance; } 

Ahora que entendemos qué es un singleton, veamos cómo encaja un singleton en el patrón del observador. El patrón de observador se usa para que un objeto responda a los cambios de otro objeto. En el segundo escenario, tenemos cuatro controladores de vista diferentes, que todos quieren saber sobre los cambios en los datos subyacentes. Los “datos subyacentes” deberían pertenecer a una sola instancia, un singleton. El “conocimiento sobre los cambios” se logra al observar los cambios realizados en el singleton.

La aplicación de inventario de la casa tendría una sola instancia de una clase que está diseñada para administrar una lista de artículos de inventario. El gerente administraría una colección de artículos para el hogar. La siguiente es una definición de clase para el administrador de datos:

 #import  @class JGCHouseholdInventoryItem; @interface HouseholdInventoryManager : NSObject /*! The global singleton for accessing application data */ + (HouseholdInventoryManager*) sharedManager; - (NSArray *) entireHouseholdInventory; - (NSArray *) luxuryItems; - (NSArray *) nonInsuredItems; - (void) addHouseholdItemToHomeInventory:(JGCHouseholdInventoryItem*)item; - (void) editHouseholdItemInHomeInventory:(JGCHouseholdInventoryItem*)item; - (void) deleteHoueholdItemFromHomeInventory:(JGCHouseholdInventoryItem*)item; @end 

Cuando cambia la recostackción de elementos del inventario de la vivienda, los controladores de visualización deben conocer este cambio. La definición de clase anterior no hace obvio cómo sucederá esto. Necesitamos seguir el patrón del observador. Los controladores de vista deben observar formalmente el sharedManager. Hay dos formas de observar otro objeto:

  • Key-Value-Observing (KVO)
  • NSNotificationCenter.

En el escenario dos, no tenemos una sola propiedad del HouseholdInventoryManager que se pueda observar utilizando KVO. Debido a que no tenemos una sola propiedad que sea fácilmente observable, el patrón del observador, en este caso, debe implementarse utilizando NSNotificationCenter. Cada uno de los cuatro controladores de vista se suscribiría a notificaciones, y sharedManager enviaría notificaciones al centro de notificaciones cuando corresponda. El administrador de inventario no necesita saber nada sobre los controladores de vista o las instancias de cualquier otra clase que pueda estar interesada en saber cuándo cambia la colección de elementos de inventario; NSNotificationCenter se ocupa de estos detalles de implementación. Los controladores de vista simplemente se suscriben a las notificaciones, y el administrador de datos simplemente publica las notificaciones.

Muchos progtwigdores principiantes aprovechan el hecho de que siempre hay exactamente un delegado de aplicaciones en la vida de la aplicación, que es accesible a nivel mundial. Los progtwigdores principiantes usan este hecho para incluir objetos y funcionalidades en la aplicaciónDelegate como una conveniencia para acceder desde cualquier otro lugar en la aplicación. El hecho de que AppDelegate sea singleton no significa que deba reemplazar a todos los otros singletons. Esta es una práctica deficiente ya que impone demasiada carga a una clase, rompiendo buenas prácticas orientadas a objetos. Cada clase debe tener un rol claro que se explique fácilmente, a menudo solo por el nombre de la clase.

Cada vez que su delegado de aplicación comienza a hincharse, comience a eliminar la funcionalidad en singletons. Por ejemplo, Core Data Stack no debe dejarse en AppDelegate, sino que debe colocarse en su propia clase, una clase coreDataManager.

Referencias

  • Administrar el flujo de datos entre los controladores de vista
  • Pasar datos entre los controladores de vista
  • Solicitudes JSON asíncronas en Objective-C

Hay varios métodos para compartir datos.

  1. Siempre puede compartir datos usando NSUserDefaults . Establezca el valor que desea compartir con respecto a una clave de su elección y obtenga el valor de NSUserDefault asociado a esa clave en el siguiente controlador de vista.

     [[NSUserDefaults standardUserDefaults] setValue:value forKey:key] [[NSUserDefaults standardUserDefaults] objectForKey:key] 
  2. Solo puede crear una propiedad en viewcontrollerA . Cree un objeto de viewcontrollerA en viewcontrollerB y asigne el valor deseado a esa propiedad.

  3. También puedes crear delegates personalizados para esto.

Pasar datos de ViewController 2 (destino) a viewController 1 (Source) es lo más interesante. Asumiendo que uses storyBoard esas son todas las formas que descubrí:

  • Delegar
  • Notificación
  • Usuario predeterminado
  • Semifallo

Esos ya fueron discutidos aquí.

Descubrí que hay más formas:

-Utilizar devoluciones de llamadas de bloque:

prepareForSegue en el método prepareForSegue en el VC1

 NextViewController *destinationVC = (NextViewController *) segue.destinationViewController; [destinationVC setDidFinishUsingBlockCallback:^(NextViewController *destinationVC) { self.blockLabel.text = destination.blockTextField.text; }]; 

-Usando guiones gráficos Relájese (Salir)

Implemente un método con un argumento UIStoryboardSegue en VC 1, como este:

 -(IBAction)UnWindDone:(UIStoryboardSegue *)segue { } 

En el StoryBoard engancha el botón “return” al botón verde Exit (Unwind) del vc. Ahora tiene una transición que “retrocede” para que pueda usar la propiedad destinationViewController en prepareForSegue de VC2 y cambiar cualquier propiedad de VC1 antes de que vuelva.

  • Otra opción para usar storyboards Undwind (Exit): puede usar el método que escribió en VC1

     -(IBAction)UnWindDone:(UIStoryboardSegue *)segue { NextViewController *nextViewController = segue.sourceViewController; self.unwindLabel.text = nextViewController.unwindPropertyPass; } 

    Y en prepareForSegue de VC1 puede cambiar cualquier propiedad que quiera compartir.

En ambas opciones de desenrollado, puede establecer la propiedad de etiqueta del botón y marcarla en prepareForSegue.

Espero haber agregado algo a la discusión.

🙂 Saludos.

El OP no mencionó los controladores de vista, pero sí muchas de las respuestas, quise darme cuenta de lo que algunas de las nuevas características del LLVM permiten hacer esto más fácil cuando se quiere pasar datos de un controlador de vista a otro y luego obteniendo algunos resultados.

Los bloques de guiones gráficos, ARC y LLVM hacen que esto sea más fácil que nunca. Algunas respuestas arriba mencionadas guiones y secuencias ya se han basado en la delegación. Definir delegates ciertamente funciona, pero algunas personas pueden encontrar más fácil pasar punteros o bloques de código.

With UINavigators and segues, there are easy ways of passing information to the subservient controller and getting the information back. ARC makes passing pointers to things derived from NSObjects simple so if you want the subservient controller to add/change/modify some data for you, pass it a pointer to a mutable instance. Blocks make passing actions easy so if you want the subservient controller to invoke an action on your higher level controller, pass it a block. You define the block to accept any number of arguments that makes sense to you. You can also design the API to use multiple blocks if that suits things better.

Here are two trivial examples of the segue glue. The first is straightforward showing one parameter passed for input, the second for output.

 // Prepare the destination view controller by passing it the input we want it to work on // and the results we will look at when the user has navigated back to this controller's view. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { [[segue destinationViewController] // This parameter gives the next controller the data it works on. segueHandoffWithInput:self.dataForNextController // This parameter allows the next controller to pass back results // by virtue of both controllers having a pointer to the same object. andResults:self.resultsFromNextController]; } 

This second example shows passing a callback block for the second argument. I like using blocks because it keeps the relevant details close together in the source – the higher level source.

 // Prepare the destination view controller by passing it the input we want it to work on // and the callback when it has done its work. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { [[segue destinationViewController] // This parameter gives the next controller the data it works on. segueHandoffWithInput:self.dataForNextController // This parameter allows the next controller to pass back results. resultsBlock:^(id results) { // This callback could be as involved as you like. // It can use Grand Central Dispatch to have work done on another thread for example. [self setResultsFromNextController:results]; }]; } 

If you want to pass data from one controller to other try this code

FirstViewController.h

 @property (nonatomic, retain) NSString *str; 

SecondViewController.h

 @property (nonatomic, retain) NSString *str1; 

FirstViewController.m

 - (void)viewDidLoad { // message for the second SecondViewController self.str = @"text message"; [super viewDidLoad]; } -(IBAction)ButtonClicked { SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil]; secondViewController.str1 = str; [self.navigationController pushViewController:secondViewController animated:YES]; } 

I was searching this solution for long time, Atlast I found it. First of all declare all the objects in your SecondViewController.h file like

 @interface SecondViewController: UIviewController { NSMutableArray *myAray; CustomObject *object; } 

Now in your implementation file allocate the memory for those objects like this

 - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization myAray=[[NSMutableArray alloc] init]; object=[[CustomObject alloc] init]; } return self; } 

Now you have allocated the memory for Array and object. now you can fill that memory before pushing this ViewController

Go to your SecondViewController.h and write two methods

 -(void)setMyArray:(NSArray *)_myArray; -(void)setMyObject:(CustomObject *)_myObject; 

in implementation file you can implement the function

 -(void)setMyArray:(NSArray *)_myArray { [myArra addObjectsFromArray:_myArray]; } -(void)setMyObject:(CustomObject *)_myObject { [object setCustomObject:_myObject]; } 

expecting that your CustomObject must have a setter function with it.

now your basic work is done. go to the place where you want to push the SecondViewController and do the following stuff

 SecondViewController *secondView= [[SecondViewController alloc] initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]] ; [secondView setMyArray:ArrayToPass]; [secondView setMyObject:objectToPass]; [self.navigationController pushViewController:secondView animated:YES ]; 

Take care for spelling mistakes.

This is not the way to do it, you should use delegates, I’ll assume we have two view controllers ViewController1 and ViewController2 and this check thing is in the first one and when its state changes, you want to do something in ViewController2, to achieve that in the proper way, you should do the below:

Add a new file to your project (Objective-C Protocol) File -> New, now name it ViewController1Delegate or whatever you want and write these between the @interface and @end directives

 @optional - (void)checkStateDidChange:(BOOL)checked; 

Now go to ViewController2.h and add

 #import "ViewController1Delegate.h" 

then change its definition to

 @interface ViewController2: UIViewController 

Now go to ViewController2.m and inside the implementation add:

 - (void)checkStateDidChange:(BOOL)checked { if (checked) { // Do whatever you want here NSLog(@"Checked"); } else { // Also do whatever you want here NSLog(@"Not checked"); } } 

Now go to ViewController1.h and add the following property:

 @property (weak, nonatomic) id delegate; 

Now if you are creating ViewController1 inside ViewController2 after some event, then you should do it this way using NIB files:

 ViewController1* controller = [[NSBundle mainBundle] loadNibNamed:@"ViewController1" owner:self options:nil][0]; controller.delegate = self; [self presentViewController:controller animated:YES completion:nil]; 

Now you are all set, whenever you detect the event of check changed in ViewController1, all you have to do is the below

 [delegate checkStateDidChange:checked]; // You pass here YES or NO based on the check state of your control 

Please tell me if there’s anything that’s not clear of if I didn’t understand your question properly.

If you want to send data from one to another viewController, here’s a way to it:

Say we have viewControllers: viewControllerA and viewControllerB

Now in viewControllerB.h

 @interface viewControllerB : UIViewController { NSString *string; NSArray *array; } - (id)initWithArray:(NSArray)a andString:(NSString)s; 

In viewControllerB.m

 #import "viewControllerB.h" @implementation viewControllerB - (id)initWithArray:(NSArray)a andString:(NSString)s { array = [[NSArray alloc] init]; array = a; string = [[NSString alloc] init]; string = s; } 

In viewControllerA.m

 #import "viewControllerA.h" #import "viewControllerB.h" @implementation viewControllerA - (void)someMethod { someArray = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil]; someString = [NSString stringWithFormat:@"Hahahahaha"]; viewControllerB *vc = [[viewControllerB alloc] initWithArray:someArray andString:someString]; [self.navigationController pushViewController:vc animated:YES]; [vc release]; } 

So this is how you can pass data from viewControllerA to viewControllerB without setting any delegate. 😉

1. Create the instance of first View Controller in the second View Controller and make its property @property (nonatomic,assign) .

2. Assign the SecondviewController instance of this view controller.

2. When you finish the selection operation copy the array to first View Controller,When u unload the SecondView ,FirstView will hold the Array Data.

Hope This Helps.

Passing Data between FirstViewController to SecondViewController as below

Por ejemplo:

FirstViewController String value as

 StrFirstValue = @"first"; 

so we can pass this value in second class using below step

1>We need to crate string object in SecondViewController.h file

 NSString *strValue; 

2>Need to declare property as below below declaration in .h file

 @property (strong, nonatomic) NSString *strSecondValue; 

3>Need synthesize that value in FirstViewController.m file below header declaration

 @synthesize strValue; 

and in FirstViewController.h :

 @property (strong, nonatomic) NSString *strValue; 

4>In FirstViewController,From which method we navigate to second view please write below code in that method.

 SecondViewController *secondView= [[SecondViewController alloc] initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]]; [secondView setStrSecondValue:StrFirstValue]; [self.navigationController pushViewController:secondView animated:YES ]; 

I am currently contributing to an open source solution to this problem through a project called MCViewFactory, which may be found here:

https://github.com/YetiHQ/manticore-iosviewfactory

The idea is imitate Android’s intent paradigm, using a global factory to manage which view you are looking at and using “intents” to switch and pass data between views. All the documentation is on the github page, but here are some highlights:

You setup all your views in .XIB files and register them in the app delegate, while initializing the factory.

 // Register activities MCViewFactory *factory = [MCViewFactory sharedFactory]; // the following two lines are optional. [factory registerView:@"YourSectionViewController"]; 

Now, in your VC, anytime you want to move to a new VC and pass data, you create a new intent and add data to its dictionary (savedInstanceState). Then, just set the current intent of factory:

 MCIntent* intent = [MCIntent intentWithSectionName:@"YourSectionViewController"]; [intent setAnimationStyle:UIViewAnimationOptionTransitionFlipFromLeft]; [[intent savedInstanceState] setObject:@"someValue" forKey:@"yourKey"]; [[intent savedInstanceState] setObject:@"anotherValue" forKey:@"anotherKey"]; // ... [[MCViewModel sharedModel] setCurrentSection:intent]; 

All of your views that conform to this need to be subclasses of MCViewController, which allow you to override the new onResume: method, allowing you access to the data you’ve passed in.

 -(void)onResume:(MCIntent *)intent { NSObject* someValue = [intent.savedInstanceState objectForKey:@"yourKey"]; NSObject* anotherValue = [intent.savedInstanceState objectForKey:@"anotherKey"]; // ... // ensure the following line is called, especially for MCSectionViewController [super onResume:intent]; } 

Hope some of you find this solution useful/interesting.

In my case I used a singleton class which can work as a global object allowing accesses to the data from almost everywhere in the app. First thing is to build a singleton class. Please refer to the page,” What should my Objective-C singleton look like? ” And what I did to make the object globally accessible was simply import it in appName_Prefix.pch which is for applying import statement in every classes. To access this object and to use, I simply implemented class method to return the shared instance, which contains its own variables

Create the property on next view controller .h and define getter and setter.

Add this property in NextVC.h on nextVC

 @property (strong, nonatomic) NSString *indexNumber; 

Añadir

@synthesize indexNumber; in NextVC.m

And last

 NextVC *vc=[[NextVC alloc]init]; vc.indexNumber=@"123"; [self.navigationController vc animated:YES]; 

I know this is a beaten subject but for those looking to answer this question with a SWIFT slant and want a bare-bones example, here my go-to method for passing data if you are using a segue to get around.

It is similar to the above but without the buttons, labels and such. Just simply passing data from one view to the next.

Setup The Storyboard

There are three parts.

  1. The Sender
  2. The Segue
  3. The Receiver

This is a very simple view layout with a segue between them.


Very simple view layout.  Note : No navigation controller


Here is the setup for the sender


The Sender


Here is the setup for the receiver.


The Receiver


Lastly, the setup for the segue.


The Segue Identifier


The View Controllers

We are keeping this simple so no buttons, not actions, we are simply moving data from the sender to the receiver when the application loads and then outputting the transmitted value to the console.

This page takes the initially loaded value and passes it along.

 // // ViewControllerSender.swift // PassDataBetweenViews // // Created by Chris Cantley on 8/25/15. // Copyright (c) 2015 Chris Cantley. All rights reserved. // import UIKit class ViewControllerSender: UIViewController { // THE STUFF - put some info into a variable let favoriteMovie = "Ghost Busters" override func viewDidAppear(animated: Bool) { // PASS IDENTIFIER - go to the recieving view controller. self.performSegueWithIdentifier("goToReciever", sender: self) } override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { //GET REFERENCE - ...to the receiver view. var viewControllerReceiver = segue.destinationViewController as? ViewControllerReceiver //PASS STUFF - pass the variable along to the target. viewControllerReceiver!.yourFavMovie = self.favoriteMovie } } 

This page just sends the value of the variable to the console when it loads. By this point our favorite movie should be in that variable.

 // // ViewControllerReceiver.swift // PassDataBetweenViews // // Created by Chris Cantley on 8/25/15. // Copyright (c) 2015 Chris Cantley. All rights reserved. // import UIKit class ViewControllerReceiver: UIViewController { //Basic empty variable waiting for you to pass in your fantastic favorite movie. var yourFavMovie = "" override func viewDidLoad() { super.viewDidLoad() //And now we can view it in the console. println("The Movie is \(self.yourFavMovie)") } } 

That is how you can tackle it if you want to use a segue and you don’t have your pages under a navigation controller.

Once it is run it should switch to the receiver view automatically and pass the value from the sender to the receiver, displaying the value in the console.

Ghost Busters is a classic folks.

NewsViewController

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tbl_View deselectRowAtIndexPath:indexPath animated:YES]; News *newsObj = [newstitleArr objectAtIndex:indexPath.row]; NewsDetailViewController *newsDetailView = [[NewsDetailViewController alloc] initWithNibName:@"NewsDetailViewController" bundle:nil]; newsDetailView.newsHeadlineStr = newsObj.newsHeadline; [self.navigationController pushViewController:newsDetailView animated:YES]; } 

NewsDetailViewController.h

 @interface NewsDetailViewController : UIViewController @property(nonatomic,retain) NSString *newsHeadlineStr; @end 

NewsDetailViewController.m

 @synthesize newsHeadlineStr; 

Delegation is the only one solution to perform such operations when you are using .xib files however all answers described above are for storyboard for .xibs files you need to use delegation. that’s only solution you can.

Another solution is use singleton class pattern initialize it once and use it in your entire app.

if you wants to pass data from ViewControlerOne to ViewControllerTwo try these..

do these in ViewControlerOne.h

  @property (nonatomic, strong) NSString *str1; 

do these in ViewControllerTwo.h

  @property (nonatomic, strong) NSString *str2; 

Synthesize str2 in ViewControllerTwo.m

 @interface ViewControllerTwo () @end @implementation ViewControllerTwo @synthesize str2; 

do these in ViewControlerOne.m

  - (void)viewDidLoad { [super viewDidLoad]; // Data or string you wants to pass in ViewControllerTwo.. self.str1 = @"hello world"; } 

on the buttons click event do this..

 -(IBAction)ButtonClicked { //Navigation on buttons click event from ViewControlerOne to ViewControlerTwo with transferring data or string.. ViewControllerTwo *objViewTwo=[self.storyboard instantiateViewControllerWithIdentifier:@"ViewControllerTwo"]; obj.str2=str1; [self.navigationController pushViewController: objViewTwo animated:YES]; } 

do these in ViewControllerTwo.m

 - (void)viewDidLoad { [super viewDidLoad]; NSLog(@"%@",str2); } 

You can save data in App delegate to access it across view controllers in your application. All you have to do is create a shared instance of app delegate

 AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate; 

For Example

if you declare a NSArray object *arrayXYZ then you can access it in any view controller by appDelegate.arrayXYZ

There are tons of ways to do this and it’s important to pick the right one. Probably one of the biggest architectural decisions lies on how the model code will be shared or accessed throughout the app.

I wrote a blog post about this a while back: Sharing Model Code . Here’s a brief summary:

Shared data

One approach is to share pointers to the model objects between view controllers.

  • Brute force iteration on view controllers (in Navigation or Tab Bar Controller) to set the data
  • Set data in prepareForSegue (if storyboards) or init (if programmatic)

Since prepare for segue is the most common here is an example:

 override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { var next = segue.destinationViewController as NextViewController next.dataSource = dataSource } 

Independent access

Another approach is to handle a screen full of data at a time and instead of coupling the view controllers to each other couple each view controller to single data source that they can get to independently.

The most common way I’ve seen this done is a singleton instance. So if your singleton object was DataAccess you could do the following in the viewDidLoad method of UIViewController:

 func viewDidLoad() { super.viewDidLoad() var data = dataAccess.requestData() } 

There are addition tools that also help pass along data:

  • Key-Value Observing
  • NSNotification
  • Datos principales
  • NSFetchedResultsController
  • Fuente de datos

Datos principales

The nice thing about Core Data is that it has inverse relationships. So if you want to just give a NotesViewController the notes object you can because it’ll have an inverse relationship to something else like the notebook. If you need data on the notebook in the NotesViewController you can walk back up the object graph by doing the following:

 let notebookName = note.notebook.name 

Read more about this in my blog post: Sharing Model Code

If you want to send data from one to another viewController, here’s a way to it:

Say we have viewControllers: ViewController and NewViewController.

in ViewController.h

 #import  @interface ViewController : UIViewController { IBOutlet UITextField *mytext1,*mytext2,*mytext3,*mytext4; } @property (nonatomic,retain) IBOutlet UITextField *mytext1,*mytext2,*mytext3,*mytext4; -(IBAction)goToNextScreen:(id)sender; @end 

in ViewController.m

 #import "ViewController.h" #import "NewViewController.h" @implementation ViewController @synthesize mytext1,mytext2,mytext3,mytext4; -(IBAction)goToNextScreen:(id)sender { NSArray *arr = [NSArray arrayWithObjects:mytext1.text,mytext2.text,mytext3.text,mytext4.text, nil]; NewViewController *newVc = [[NewViewController alloc] initWithNibName:@"NewViewController" bundle:nil]; newVc.arrayList = arr; [self.navigationController pushViewController:newVc animated:YES]; } 

In NewViewController.h

 #import  @interface NewViewController : UITableViewController { NSArray *arrayList; NSString *name,*age,*dob,*mobile; } @property(nonatomic, retain)NSArray *arrayList; @end 

In NewViewController.m

 #import "NewViewController.h" #import "ViewController.h" @implementation NewViewController @synthesize arrayList; #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // Return the number of sections. return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Return the number of rows in the section. return [arrayList count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } // Configure the cell... cell.textLabel.text = [arrayList objectAtIndex:indexPath.row]; return cell; } @end 

So this way we can pass the data from one viewcontroller to another view controller…

I like the idea of Model objects and Mock objects based on NSProxy to commit or discard data if what user selects can be cancelled.

It’s easy to pass data around since it’s single object or couple of objects and if you have let’s say UINavigationController controller, you can keep the reference to model inside and all pushed view controllers can access it directly from navigation controller.

I have seen a lot of people over complicating this using the didSelectRowAtPath method. I am using Core Data in my example.

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ //this solution is for using Core Data YourCDEntityName * value = (YourCDEntityName *)[[self fetchedResultsController] objectAtIndexPath: indexPath]; YourSecondViewController * details = [self.storyboard instantiateViewControllerWithIdentifier:@"nameOfYourSecondVC"];//make sure in storyboards you give your second VC an identifier //Make sure you declare your value in the second view controller details.selectedValue = value; //Now that you have said to pass value all you need to do is change views [self.navigationController pushViewController: details animated:YES]; } 

4 lines of code inside the method and you are done.

This is a really great tutorial for anyone that wants one. Here is the example code:

 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.identifier isEqualToString:@"myIdentifer]) { NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; myViewController *destViewController = segue.destinationViewController; destViewController.name = [object objectAtIndex:indexPath.row]; } } 

There are many answers to this questions offering many different ways to perform view controller communication that would indeed work, but I don’t see anywhere mentioned which one are actually best to use and which ones to avoid.

In practice, in my opinion only a few solutions are recommended:

  • To pass data forward:
    • override the prepare(for:sender:) method of UIViewController when using a storyboard and segues
    • pass data through an initializer or through properties when performing view controller transitions thtough code
  • To pass data backwards
    • update the app shared state (which you can pass forward between view controllers with either one of the methods above)
    • use delegation
    • use an unwind segue

Solutions I recommend NOT to use:

  • Referencing the previous controller directly instead of using delegation
  • Sharing data through a singleton
  • Passing data through the app delegate
  • Sharing data through the user defaults
  • Passing data through notifications

These solutions, although working in the short term, introduce too many dependencies that will garble the architecture of the app and create more problems later.

For those interested, I wrote some articles that address these points more in depth and highlight the various drawbacks:

  • How iOS View Controllers Communicate With Each Other
  • How to Structure the Code of iOS Apps
  • Understanding the Core Architectural Principles of iOS Development with a Practical Example