Transmisión de datos entre controlleres de vista

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

Tengo una vista que actúa como un formulario de ingreso de datos y quiero darle al usuario la opción de seleccionar varios productos. Los productos se enumeran en otra vista con un controller UITableView y he habilitado varias selects.

Mi pregunta es: ¿cómo transfiero los datos de una vista a otra? UITableView las selects en el UITableView en una matriz, pero ¿cómo puedo pasarlas a la vista de formulario de input de datos anterior para que pueda savese junto con los demás datos a los datos centrales al enviar el formulario?

He navegado y visto que algunas personas declaran una matriz en el delegado de la aplicación. Leí algo sobre Singletons, pero no entiendo lo que son y leí algo acerca de cómo crear un model 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é que intentaría 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 para que la gente entienda y que no me haya perdido nada.

Pasar datos hacia adelante

Pasar datos hacia un controller de vista desde otro controller de vista. Utilizaría este método si desea pasar un object / valor desde un controller de vista a otro controller de vista que puede estar presionando a una stack de navigation.

Para este ejemplo, tendremos ViewControllerA y ViewControllerB

Para pasar un valor BOOL de ViewControllerA a ViewControllerB haremos lo siguiente.

  1. en ViewControllerB.h crea una propiedad para BOOL

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

     #import "ViewControllerB.h" 

    Entonces, donde desea cargar la vista, por ejemplo. didSelectRowAtIndex o alguna IBAction que necesita para establecer la propiedad en ViewControllerB antes de empujarlo a la stack de naves.

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

    Esto establecerá isSomethingEnabled en ViewControllerB a BOOL value YES .

Pasar datos hacia adelante usando Segues

Si está utilizando Storyboards, es muy probable que use segues y necesitará este procedimiento para pasar los datos hacia adelante. Esto es similar a lo anterior, pero en lugar de pasar los datos antes de empujar el controller de vista, usa 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 tiene que contarle acerca de ViewControllerB así que use un

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

  4. A continuación, debemos agregar el método a ViewControllerA que se llama cuando se realiza una segue, debido a esto debemos detectar a qué segue se llamó y luego hacer algo. En nuestro ejemplo, verificaremos "showDetailSegue" y si eso se realiza, pasaremos nuestro valor de 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 puntos de vista embeddeds en un controller de navigation, debe cambiar ligeramente el método anterior a lo 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 value YES .

Pasando datos atrás

Para pasar datos de ViewControllerB a ViewControllerA necesita usar Protocolos y Delegados o Bloques , este último se puede usar como un mecanismo acoplado para las devoluciones de llamada.

Para hacer esto haremos de ViewControllerA un delegado de ViewControllerB . Esto permite que ViewControllerB envíe un post a ViewControllerA que nos permite enviar datos de vuelta.

Para que ViewControllerA sea ​​delegado de ViewControllerB , debe cumplir con el protocolo ViewControllerB que tenemos que especificar. Esto le dice a ViewControllerA qué methods debe implementar.

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

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

     @property (nonatomic, weak) id <ViewControllerBDelegate> delegate; 
  3. En ViewControllerB llamamos a un post en el delegate cuando desplegaremos el controller 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 , diga a ViewControllerA que importe ViewControllerB y que se ajuste a su protocolo.

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

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

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

Referencias

  • Uso de la delegación para comunicarse con otros controlleres de vista en la Guía de progtwigción del controller de vista
  • Patrón de delegado

La M en MVC es para "Modelo" y en el paradigma MVC el rol de las classs de model es administrar los datos de un progtwig. Un model 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 model sabe todo sobre cómo trabajar con los datos, pero nada sobre cómo mostrarlos. Los models pueden ser complicados, pero no tienen por qué serlo: el model para su aplicación puede ser tan simple como una serie de cadenas o dictionarys.

El papel de un controller es mediar entre la vista y el model. Por lo tanto, necesitan una reference a uno o más objects de vista y uno o más objects de model. Digamos que su model es una variedad de dictionarys, con cada dictionary que representa una fila en su tabla. La vista raíz de su aplicación muestra esa tabla, y es posible que sea responsable de cargar la matriz desde un file. Cuando el usuario decide agregar una nueva fila a la tabla, tocan un button y su controller crea un nuevo dictionary (mutable) y lo agrega a la matriz. Para completar la fila, el controller crea un controller de vista de detalle y le otorga el nuevo dictionary. El controller de vista de detalle llena el dictionary y vuelve. El dictionary ya forma parte del model, por lo que no tiene que pasar nada más.

Rápido

Actualizado para Swift 3

Hay toneladas y toneladas de explicaciones aquí y alnetworkingedor de StackOverflow, pero si eres un principiante tratando de hacer que algo básico funcione, testing a ver este tutorial de YouTube (es lo que me ayudó a entender finalmente cómo hacerlo).

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

Pasar los datos hacia el próximo controller de vista

El siguiente es un ejemplo basado en el video. La idea es pasar una cadena desde el campo de text en el primer controller de vista a la label en el segundo controller de vista.

introduzca la descripción de la imagen aquí

Cree el layout del guión gráfico en el Generador de interfaces. Para hacer el segue, simplemente click el button y arrástrelo hasta el segundo controller de vista.

Primer controller de vista

El código para el primer controller de vista 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 controller 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 olvides

  • UITextField los puntos de venta para UITextField y UILabel .
  • Establezca el primer y segundo View Controllers en los files Swift apropiados en IB.

Transmisión de datos al controller de vista anterior

Para transferir datos desde el segundo controller de vista al primer controller de vista, usa un protocolo y un delegado . Este video es un recorrido muy claro de ese process:

  • Tutorial de YouTube: tutorial de conceptos básicos de iOS Swift: protocolos y delegates Pero también lea esta publicación para asegurarse de que no ingrese en un ciclo de reference sólido.

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

introduzca la descripción de la imagen aquí

Cree el layout del guión gráfico en el Generador de interfaces. Nuevamente, para hacer la segue, simplemente controle arrastre desde el button hasta el segundo controller de vista. Establezca el identificador segue para showSecondViewController . Además, no olvide conectar las salidas y las acciones usando los nombres en el siguiente código.

Primer controller de vista

El código para el primer controller de vista es

 import UIKit class FirstViewController: UIViewController, DataEntenetworkingDelegate { @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 count el uso de nuestro protocolo personalizado DataEntenetworkingDelegate .

Segundo controller y protocolo de vista

El código para el segundo controller de vista es

 import UIKit // protocol used for sending data back protocol DataEntenetworkingDelegate: 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: DataEntenetworkingDelegate? = 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 count que el protocol está fuera de la class Controlador de vista.

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

Existen varias forms en que se pueden recibir datos en una class diferente en iOS. Por ejemplo –

  1. Inicialización directa después de la asignación de otra class.
  2. Delegación: para pasar datos atrás
  3. Notificación: para transmitir datos a múltiples classs 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 simple escenario de pasar un valor a una class diferente cuya asignación se realiza en la class actual, el método más común y preferido sería la configuration directa de valores después de la asignación. Esto se hace de la siguiente manera:

Podemos entenderlo usando dos controlleres: Controller1 y Controller2.

Supongamos que en la class Controller1 desea crear el object Controller2 y presionarlo con un valor de cadena 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 class Controller2, habrá esta function 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 properties de la class 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 múltiples parameters como:

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

O si necesita pasar más de 3 parameters que están relacionados con una característica común, puede almacenar los valores en una class de Modelo y pasar ese model a la siguiente class

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

Entonces, en breve 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, parecía que los protocolos y los delegates eran la forma correcta / prefería Apple de hacerlo.

Terminé usando este ejemplo

Compartir datos entre controlleres de vista y otros objects @ iPhone Dev SDK

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

Gracias por toda tu ayuda

Encuentro la versión más simple y elegante con bloques que pasan. Nombreemos el controller de vista que espera los datos devueltos como "A" y el controller de vista de retorno como "B". En este ejemplo, queremos get 2 valores: primero de Type1 y segundo de Type2.

Suponiendo que usemos Storyboard, el primer controller establece el bloque 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 controller 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); 

En el file de implementación BViewController.m, una vez que tengamos los valores deseados para devolver nuestra callback, debemos llamar:

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

Una cosa para recordar es que el uso del bloque a menudo necesita gestionar references fuertes y __dilugentes como se explica aquí

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

La pregunta se refiere a pasar información entre los controlleres de vista. El ejemplo específico que se le pregunta acerca de cómo pasar información entre las vistas, pero dada la novedad autodeclarada de iOS, es probable que el cartel original se refiera a viewControllers, no a las vistas (sin la participación de ViewControllers). Parece que todas las respuestas se centran en dos controlleres de vista, pero ¿qué sucede si la aplicación evoluciona para necesitar involucrar a más de dos controlleres de vista en el intercambio de información?

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

Para ayudar a cualquier otra persona a ver esta pregunta, quién quiere una respuesta completa, voy a tratar de proporcionarla.

Escenarios de aplicación

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

Escenario uno: el máximo de dos controlleres de vista necesita compartir información. Vea el diagtwig uno.

diagrama del problema original

Hay dos controlleres de vista en la aplicación. Hay un ViewControllerA (formulario de input de datos) y View Controller B (Lista de productos). Los elementos seleccionados en la list de productos deben coincidir con los elementos que se muestran en el textbox en el formulario de input de datos. En este escenario, ViewControllerA y ViewControllerB deben comunicarse directamente entre sí y sin otros controlleres de vista.

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

diagrama de aplicación de inventario de vivienda

Hay cuatro controlleres de vista en la aplicación. Es una aplicación basada en tabs para administrar el deviseio de la casa. Tres controlleres de vista presentan vistas diferentes filtradas de los mismos datos:

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

Cada vez que se crea o edita un elemento individual, también debe sincronizarse con los otros controlleres de vista. Por ejemplo, si agregamos un barco en ViewControllerD, pero aún no está asegurado, entonces el barco debe aparecer cuando el usuario vaya a ViewControllerA (artículos de lujo), y también ViewControllerC (Entire Home Inventory), 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 estar permitidos desde cualquiera de los cuatro controlleres de visualización) o de editar elementos existentes (que pueden estar permitidos desde el "Agregar nuevo formulario de elemento", reutilizando los mismos para para editar).

Dado que todos los controlleres de vista necesitan compartir los mismos datos, los cuatro controlleres de vista deben permanecer en synchronization, y por lo tanto, debe haber algún tipo de comunicación con todos los demás controlleres de vista, siempre que cualquier controller de vista única cambie los datos subyacentes. Debería ser bastante obvio que no queremos que cada controller de vista se comunique directamente con el controller de vista en este escenario. En caso de que no sea obvio, considere si teníamos 20 controlleres 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 controlleres de vista cada vez que un controller de vista hizo un cambio?

The Solutions: Delegates and the Observer Pattern, y Singletons

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

  • segues
  • delegates
  • Configurar properties directamente en los controlleres de vista.
  • NSUserDefaults (en realidad una mala elección)

En el segundo escenario, tenemos otras soluciones viables:

  • Patrón de observador
  • Singletons

Un singleton es una instancia de una class, esa instancia es la única instancia en existencia durante su vida. Un singleton obtiene su nombre del hecho de que es la instancia única. Normalmente, los desarrolladores que usan singletons tienen methods de class especiales para acceder a ellos.

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

Ahora que entendemos lo que es un singleton, vamos a discutir cómo un singleton se ajusta al patrón del observador. El patrón de observador se usa para que un object responda a los cambios de otro object. En el segundo escenario, tenemos cuatro controlleres de vista diferentes, que todos quieren saber acerca de los cambios en los datos subyacentes. Los "datos subyacentes" deberían pertenecer a una única instancia, un singleton. El "conocimiento sobre los cambios" se logra observando los cambios realizados en el singleton.

La aplicación de deviseio de origen tendría una única instancia de una class que está diseñada para administrar una list de artículos de deviseio. El gerente administraría una colección de artículos para el hogar. La siguiente es una definición de class para el administrador de datos:

 #import <Foundation/Foundation.h> @class JGCHouseholdInventoryItem; @interface HouseholdInventoryManager : NSObject /*! The global singleton for accessing application data */ + (HouseholdInventoryManager*) shanetworkingManager; - (NSArray *) entireHouseholdInventory; - (NSArray *) luxuryItems; - (NSArray *) nonInsunetworkingItems; - (void) addHouseholdItemToHomeInventory:(JGCHouseholdInventoryItem*)item; - (void) editHouseholdItemInHomeInventory:(JGCHouseholdInventoryItem*)item; - (void) deleteHoueholdItemFromHomeInventory:(JGCHouseholdInventoryItem*)item; @end 

Cuando la colección de artículos del deviseio de la casa cambia, los controlleres de la vista deben ser conscientes de este cambio. La definición de class anterior no hace que sea obvio cómo sucederá. Debemos seguir el patrón del observador. Los controlleres de vista deben observar formalmente el administrador compartido. Hay dos forms de observar otro object:

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

En el segundo escenario, no tenemos una sola propiedad de HouseholdInventoryManager que se pueda observar utilizando KVO. Debido a que no tenemos una sola propiedad que sea fácilmente observable, el patrón de observador en este caso debe implementarse usando NSNotificationCenter. Cada uno de los cuatro controlleres de vista se suscribiría a las notifications, y el administrador compartido enviaría notifications al centro de notifications cuando corresponda. El administrador de deviseio no necesita saber nada sobre los controlleres de vista o las instancias de otras classs que puedan estar interesadas en saber cuándo cambia la colección de artículos de deviseio; NSNotificationCenter se encarga de estos detalles de implementación. Los controlleres de vista simplemente se suscriben a las notifications, y el administrador de datos simplemente publica notifications.

Muchos progtwigdores principiantes aprovechan el hecho de que siempre hay exactamente un Delegado de Aplicación en la vida útil de la aplicación, que es accesible a nivel mundial. Los progtwigdores principiantes usan este hecho para rellenar objects y funcionalidad en la aplicaciónDelegate como una conveniencia para acceder desde cualquier otro lugar de la aplicación. El hecho de que AppDelegate sea un singleton no significa que deba replace a todos los demás singletons. Esta es una mala práctica, ya que pone demasiada carga en una class, rompiendo buenas prácticas orientadas a objects. Cada class debe tener un papel claro que se explica fácilmente, a menudo solo con el nombre de la class.

Cada vez que su Delegado de la aplicación comienza a hincharse, comience a eliminar la funcionalidad en singletons. Por ejemplo, Core Data Stack no se debe dejar en AppDelegate, sino que debe includese en su propia class, una class coreDataManager.

Referencias

  • Gestión del flujo de datos entre los controlleres de vista
  • Pasar datos entre controlleres de vista
  • Solicitudes de JSON asincrónicas en Objective-C

Existen múltiples methods para compartir datos.

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

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

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

Pasar datos de nuevo desde ViewController 2 (destino) para verController 1 (Source) es lo más interesante. Suponiendo que uses storyboard estas son todas las forms que descubrí:

  • Delegar
  • Notificación
  • Fallos de usuario
  • Semifallo

Ya se discutieron aquí.

Descubrí que hay más forms:

-Uso de callbacks 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; }]; 

-Uso de storyboards Relájese (Salir)

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

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

En el gancho de StoryBoard, el button "volver" al button Salir verde (Desenrollar) del vc. Ahora tiene un segue que "regresa", por lo que puede usar la propiedad destinationViewController en prepareForSegue of VC2 y cambiar cualquier propiedad de VC1 antes de que regrese.

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

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

    And in the prepareForSegue of VC1 you can change any property you want to share.

In both unwind options you can set the tag property of the button and check it in the prepareForSegue.

Hope i added something to the discussion.

🙂 Cheers.

The OP didn't mention view controllers but so many of the answers do, that I wanted to chime in with what some of the new features of the LLVM allow to make this easier when wanting to pass data from one view controller to another and then getting some results back.

Storyboard segues, ARC and LLVM blocks make this easier than ever for me. Some answers above mentioned storyboards and segues already but still relied on delegation. Defining delegates certainly works but some people may find it easier to pass pointers or code blocks.

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<ViewController1Delegate> 

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<ViewController1Delegate> 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 shanetworkingFactory]; // 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 shanetworkingModel] 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 shanetworking 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 shanetworking instance of app delegate

 AppDelegate *appDelegate = (AppDelegate *)[UIApplication shanetworkingApplication].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 its important to pick the right one. Probably one of the biggest architectural decisions lies on how model code will be shanetworking or accessed throughout the app.

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

Shanetworking 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 through 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
  • Core Data
  • NSFetchedResultsController
  • Fuente de datos

Core Data

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 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 on 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 <UIKit/UIKit.h> @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 <UIKit/UIKit.h> @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.

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 shanetworking 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

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