Pasar datos entre controladores de vista
Soy nuevo en iOS y Objective-C y en todo el paradigma MVC y estoy atascado 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 varios productos. Los productos se enumeran en otra vista con UITableViewController
y he habilitado varias selecciones.
Mi pregunta es, ¿cómo transfiero los datos de una vista a otra? Mantendré las selecciones UITableView
en una matriz, pero ¿cómo puedo luego pasar eso a la vista anterior del formulario de ingreso de datos para que pueda guardarse junto con los otros datos en Core Data al enviar el formulario?
Navegué y vi a algunas personas declarar una matriz en el delegado de la aplicación. Leí algo sobre Singletons pero no entiendo qué son y leí algo sobre la creación de un modelo de datos.
¿Cuál sería la forma correcta de realizar esto y cómo lo haría?
Respuestas
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 para que la gente la entienda y que no me haya perdido nada.
Pasar datos hacia adelante
Pasar datos a un controlador de vista desde otro controlador de vista. Usaría este método si quisiera pasar un objeto / valor de un controlador de vista a otro controlador de vista que puede estar empujando a una pila de navegación.
Para este ejemplo, tendremos ViewControllerA
yViewControllerB
Para pasar un BOOL
valor de ViewControllerA
a ViewControllerB
haríamos lo siguiente.
en
ViewControllerB.h
crear una propiedad para elBOOL
@property (nonatomic, assign) BOOL isSomethingEnabled;
en
ViewControllerA
lo que necesita para decirle sobreViewControllerB
lo que utilizar una#import "ViewControllerB.h"
Luego, donde desea cargar la vista, por ejemplo.
didSelectRowAtIndex
o algunos en losIBAction
que necesita establecer la propiedadViewControllerB
antes de colocarla en la pila de navegación.ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil]; viewControllerB.isSomethingEnabled = YES; [self pushViewController:viewControllerB animated:YES];
Esto establecerá
isSomethingEnabled
enViewControllerB
alBOOL
valorYES
.
Pasar datos hacia adelante usando Segues
Si está utilizando Storyboards, lo más probable es que esté utilizando segues y necesitará este procedimiento para transmitir los datos. Esto es similar a lo anterior, pero en lugar de pasar los datos antes de presionar el controlador 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:
en
ViewControllerB.h
crear una propiedad para elBOOL
@property (nonatomic, assign) BOOL isSomethingEnabled;
en
ViewControllerA
lo que necesita para decirle sobreViewControllerB
lo que utilizar una#import "ViewControllerB.h"
Cree una transición de
ViewControllerA
aViewControllerB
en el guión gráfico y asígnele un identificador, en este ejemplo lo llamaremos"showDetailSegue"
A continuación, debemos agregar el método al
ViewControllerA
que se llama cuando se realiza cualquier segue, debido a esto, necesitamos detectar qué segue se llamó y luego hacer algo. En nuestro ejemplo, lo comprobaremos"showDetailSegue"
y, si se realiza, pasaremos nuestroBOOL
valor aViewControllerB
-(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 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
enViewControllerB
alBOOL
valorYES
.
Pasando datos de vuelta
Para devolver los datos de ViewControllerB
a ViewControllerA
necesita utilizar Protocolos y Delegados o Bloques , estos últimos se pueden utilizar como un mecanismo débilmente acoplado para devoluciones de llamada.
Para ello haremos ViewControllerA
un delegado de ViewControllerB
. Esto permite ViewControllerB
enviar un mensaje de vuelta para ViewControllerA
permitirnos enviar datos.
Para ViewControllerA
ser un delegado de la ViewControllerB
misma debe ajustarse al ViewControllerB
protocolo que tenemos que especificar. Esto le dice ViewControllerA
qué métodos debe implementar.
En
ViewControllerB.h
, debajo de#import
, pero arriba@interface
, especifica el protocolo.@class ViewControllerB; @protocol ViewControllerBDelegate <NSObject> - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item; @end
A continuación, todavía en el
ViewControllerB.h
que necesita para configurar unadelegate
propiedad y sintetizar enViewControllerB.m
@property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
In
ViewControllerB
llamamos a un mensajedelegate
cuando abrimos el controlador de vista.NSString *itemToPassBack = @"Pass this value back to ViewControllerA"; [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
Eso es todo
ViewControllerB
. AhoraViewControllerA.h
, dígaleViewControllerA
que importeViewControllerB
y cumpla con su protocolo.#import "ViewControllerB.h" @interface ViewControllerA : UIViewController <ViewControllerBDelegate>
En
ViewControllerA.m
implementar el siguiente método de nuestro protocolo- (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item { NSLog(@"This was returned from ViewControllerB %@",item); }
Antes de presionar
viewControllerB
a la pila de navegación, necesitamos decirleViewControllerB
queViewControllerA
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
- Uso de la delegación para comunicarse con otros controladores de vista en la Guía de programación del controlador de vista
- Patrón de delegado
NSNotification center Es otra forma de transmitir datos.
// add observer in controller(s) where you want to receive data
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeepLinking:) name:@"handleDeepLinking" object:nil];
-(void) handleDeepLinking:(NSNotification *) notification {
id someObject = notification.object // some custom object that was passed with notification fire.
}
// post notification
id someObject;
[NSNotificationCenter.defaultCenter postNotificationName:@"handleDeepLinking" object:someObject];
Pasar datos de una clase a otra (una clase puede ser cualquier controlador, administrador de red / sesión, subclase de UIView o cualquier otra clase)
Los bloques son funciones anónimas.
Este ejemplo pasa datos del controlador B al controlador A
definir un bloque
@property void(^selectedVoucherBlock)(NSString *); // in ContollerA.h
agregue el controlador de bloques (oyente) donde necesite un valor (por ejemplo, necesita su respuesta de API en ControllerA o necesita datos de ContorllerB en A)
// in ContollerA.m
- (void)viewDidLoad {
[super viewDidLoad];
__unsafe_unretained typeof(self) weakSelf = self;
self.selectedVoucherBlock = ^(NSString *voucher) {
weakSelf->someLabel.text = voucher;
};
}
Ir al controlador B
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
ControllerB *vc = [storyboard instantiateViewControllerWithIdentifier:@"ControllerB"];
vc.sourceVC = self;
[self.navigationController pushViewController:vc animated:NO];
bloque de fuego
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath *)indexPath {
NSString *voucher = vouchersArray[indexPath.row];
if (sourceVC.selectVoucherBlock) {
sourceVC.selectVoucherBlock(voucher);
}
[self.navigationController popToViewController:sourceVC animated:YES];
}
Otro ejemplo práctico para bloques
Rápido
Hay toneladas y toneladas de explicaciones aquí y alrededor de StackOverflow, pero si eres un principiante y solo estás tratando de hacer que algo básico funcione, intenta ver este tutorial de YouTube (es lo que me ayudó a finalmente entender cómo hacerlo).
- Tutorial de YouTube: cómo enviar datos a través de segue (swift)
Pasar datos al siguiente controlador 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.
Cree el diseño del guión gráfico en Interface Builder. Para hacer la transición, simplemente Controlhaga clic en el botón y arrastre hasta el controlador de segunda vista.
Controlador de primera vista
El código para el controlador de primera 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 controlador de segunda 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
- Conecte los enchufes para el
UITextField
y elUILabel
. - Configure el primer y segundo controlador de vista en los archivos Swift apropiados en IB.
Pasar datos al controlador de vista anterior
Para devolver datos del segundo controlador de vista al primer controlador de vista, usa un protocolo y un delegado . Este video es un recorrido muy claro de ese proceso:
- Tutorial de YouTube: Tutorial básico de iOS Swift: Protocolos y delegados Pero también lea esta publicación para asegurarse de no entrar en un ciclo de referencia sólido.
El siguiente es un ejemplo basado en el video (con algunas modificaciones).
Cree el diseño del guión gráfico en Interface Builder. Nuevamente, para hacer la transición, simplemente Controlarrastre desde el botón hasta el controlador de segunda vista. Establezca el identificador de segue en showSecondViewController
. Además, no olvide conectar los puntos de venta y las acciones usando los nombres en el siguiente código.
Controlador de primera vista
El código para el controlador de primera vista 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 DataEnteredDelegate
protocolo 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: AnyObject {
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 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.
La M en MVC es para "Modelo" y en el paradigma MVC la función de las clases modelo es administrar los datos de un programa. 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 datos, pero nada sobre cómo mostrarlos. Los modelos pueden ser complicados, pero no tienen por qué serlo: el modelo de su aplicación puede ser tan simple como una serie 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 vista y uno o más objetos de modelo. Digamos que su modelo es una matriz de diccionarios, y cada diccionario representa una fila en su tabla. La vista raíz de su aplicación muestra esa tabla y podría ser responsable de cargar la matriz desde un archivo. Cuando el usuario decide agregar una nueva fila a la tabla, toca algún botón y su controlador crea un diccionario nuevo (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 de detalles completa el diccionario y regresa. El diccionario ya forma parte del modelo, por lo que no es necesario que suceda nada más.
Hay varias formas de recibir datos en una clase diferente en iOS. Por ejemplo -
- Inicialización directa después de la asignación de otra clase.
- Delegación: para devolver los datos
- Notificación: para transmitir datos a varias clases al mismo tiempo
- Guardar
NSUserDefaults
- para acceder a él más tarde - Clases singleton
- 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 el establecimiento directo de valores después de la asignación. Esto se hace de la siguiente manera:
Podemos entenderlo usando dos controladores: Controller1 y Controller2
Suponga que en la clase Controller1 desea crear el objeto Controller2 y empujarlo 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 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 manera similar a esta:
- (void)pushToController2 {
Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj setStringPassed:@"String"];
[self pushViewController:obj animated:YES];
}
Para pasar varios valores, puede utilizar varios parámetros 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 resumen, 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 investigación, parecía que Protocolos y delegados es la forma correcta / preferida por Apple de hacer esto.
Terminé usando este ejemplo
Compartir datos entre 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 de paso. Vamos a nombrar el controlador de vista que espera los datos devueltos como "A" y el controlador de vista de retorno como "B". En este ejemplo queremos obtener 2 valores: el primero de Type1 y el segundo de Type2.
Suponiendo que usamos Storyboard, el primer controlador establece el bloque de devolución de llamada, 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" debe declarar la propiedad de devolución de llamada, BViewController.h:
// it is important to use "copy"
@property (copy) void(^callback)(Type1 *value1, Type2 *value2);
Luego, en el archivo de implementación BViewController.m, después de que tengamos los valores deseados para devolver, nuestra devolución de llamada debería llamarse:
if (self.callback)
self.callback(value1, value2);
Una cosa para recordar es que el uso de bloques a menudo necesita administrar referencias fuertes y débiles como se explica aquí.
Hay buena información en muchas de las respuestas dadas, pero ninguna aborda la pregunta por completo.
La pregunta se refiere a pasar información entre controladores de vista. El ejemplo específico dado pregunta sobre el paso de información entre vistas, pero dada la novedad autodeclarada de iOS, el póster original probablemente significaba 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é pasa si la aplicación evoluciona y necesita involucrar a más de dos controladores de vista en el intercambio de información?
El cartel original también preguntó sobre Singletons y el uso de AppDelegate . Estas preguntas necesitan respuesta.
Para ayudar a cualquier otra persona que esté mirando esta pregunta, que quiera una respuesta completa, intentaré proporcionarla.
Escenarios de aplicación
En lugar de tener una discusión abstracta y altamente hipotética, es útil tener en mente aplicaciones concretas. 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: un máximo de dos controladores de vista necesitan compartir información. Vea el diagrama uno.
Hay dos controladores de vista en la aplicación. Hay un ViewControllerA (formulario de entrada de datos) y View Controller 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 no con otros controladores de vista.
Escenario dos : más de dos controladores de vista deben compartir la misma información. Vea el diagrama dos.
Hay cuatro controladores de vista en la aplicación. Es una aplicación basada en pestañas para administrar el inventario del hogar. 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 de toda la casa
- ViewControllerD - Agregar formulario de artículo nuevo
Cada vez que se crea o edita un elemento individual, también debe sincronizarse con los otros controladores 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 a ViewControllerC (Inventario de toda la casa), pero no cuando el usuario vaya a ViewControllerB (artículos no asegurados). Debemos preocuparnos no solo por agregar nuevos elementos, sino también por eliminar elementos (que pueden estar permitidos desde cualquiera de los cuatro controladores de vista) o editar elementos existentes (que pueden estar permitidos desde el "Formulario para agregar nuevos elementos", reutilizando los mismos para editar).
Dado que 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 cambie los datos subyacentes. Debería ser bastante obvio que no queremos que cada controlador de vista se comunique directamente entre sí en este escenario. En caso de que no sea obvio, considere si tuviéramos 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 realiza un cambio?
Las soluciones: los delegados y el patrón de observador, y singletons
En el escenario uno, tenemos varias soluciones viables, ya que otras respuestas han dado
- segues
- delegados
- 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 que existe durante su vida. Un singleton recibe su nombre del hecho de que es la instancia única. Normalmente, los desarrolladores que usan singletons tienen métodos de clase 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, analicemos cómo encaja un singleton en el patrón del observador. El patrón de observador se utiliza para que un objeto responda a los cambios de otro objeto. En el segundo escenario, tenemos cuatro controladores de vista diferentes, todos quieren saber acerca de los cambios en los datos subyacentes. Los "datos subyacentes" deben pertenecer a una sola instancia, un singleton. El "conocimiento de los cambios" se logra mediante la observación de los cambios realizados en el singleton.
La aplicación de inventario del hogar tendría una sola instancia de una clase que está diseñada para administrar una lista de artículos de inventario. El administrador 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 <Foundation/Foundation.h>
@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 colección de artículos del inventario del hogar, los controladores de vista deben estar al tanto de este cambio. La definición de clase anterior no deja en claro 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:
- Observación de valores-clave (KVO)
- NSNotificationCenter.
En el escenario dos, no tenemos una sola propiedad de HouseholdInventoryManager que pueda observarse usando 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 controladores de vista se suscribía a las notificaciones, y el SharedManager enviaría notificaciones al centro de notificaciones cuando fuera apropiado. El administrador de inventario no necesita saber nada sobre los controladores de vista o instancias de cualquier otra clase que pueda estar interesada en saber cuándo cambia la colección de artículos de inventario; NSNotificationCenter se encarga de estos detalles de implementación. Los controladores de vista simplemente se suscriben a las notificaciones y el administrador de datos simplemente publica notificaciones.
Muchos programadores principiantes aprovechan el hecho de que siempre hay exactamente un Delegado de aplicación durante la vida útil de la aplicación, que es accesible a nivel mundial. Los programadores principiantes utilizan este hecho para incluir objetos y funciones en appDelegate como una conveniencia para el acceso desde cualquier otro lugar de la aplicación. El hecho de que AppDelegate sea un singleton no significa que deba reemplazar a todos los demás singleton. Esta es una mala práctica ya que coloca demasiada carga en una clase, rompiendo las 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 aplicaciones comience 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 controladores de vista
- Solicitudes JSON asincrónicas en Objective-C
El OP no mencionó los controladores de vista, pero muchas de las respuestas lo hacen, que quería intervenir con algunas de las nuevas características de LLVM que permiten hacer esto más fácil cuando quiero 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 para mí. Algunas respuestas ya mencionaron guiones gráficos y segues, pero aún dependían de la delegación. Definir delegados ciertamente funciona, pero a algunas personas les puede resultar más fácil pasar punteros o bloques de código.
Con UINavigators y segues, hay formas fáciles de pasar información al controlador subordinado y recuperar la información. ARC hace que pasar punteros a cosas derivadas de NSObjects sea simple, por lo que si desea que el controlador subordinado agregue / cambie / modifique algunos datos por usted, pásele un puntero a una instancia mutable. Los bloques facilitan el paso de acciones, por lo que si desea que el controlador subordinado invoque una acción en su controlador de nivel superior, páselo un bloque. Usted define el bloque para aceptar cualquier número de argumentos que tenga sentido para usted. También puede diseñar la API para usar varios bloques si eso se adapta mejor a las cosas.
Aquí hay dos ejemplos triviales del pegamento segue. El primero es sencillo y muestra un parámetro pasado para entrada, el segundo para salida.
// 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];
}
Este segundo ejemplo muestra el paso de un bloque de devolución de llamada para el segundo argumento. Me gusta usar bloques porque mantiene los detalles relevantes juntos en la fuente, la fuente de nivel superior.
// 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];
}];
}
Pasar datos desde ViewController 2 (destino) a viewController 1 (Fuente) es lo más interesante. Suponiendo que use StoryBoard, esas son todas las formas en que descubrí:
- Delegar
- Notificación
- Valores predeterminados del usuario
- único
Aquellos ya se discutieron aquí.
Descubrí que hay más formas:
-Usando devoluciones de llamada en bloque:
Úselo en el prepareForSegue
método en el VC1
NextViewController *destinationVC = (NextViewController *) segue.destinationViewController;
[destinationVC setDidFinishUsingBlockCallback:^(NextViewController *destinationVC)
{
self.blockLabel.text = destination.blockTextField.text;
}];
-Utilizando 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, enganche el botón "volver" al botón verde Salir (Desenrollar) del vc. Ahora tiene un segue que "retrocede", por lo que puede usar la propiedad destinationViewController en prepareForSegue de VC2 y cambiar cualquier propiedad de VC1 antes de que retroceda.
Otra opción para usar guiones gráficos 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 desee compartir.
En ambas opciones de desenrollado, puede establecer la propiedad de etiqueta del botón y verificarla en prepareForSegue.
Espero haber agregado algo a la discusión.
:) Salud.
Existen varios métodos para compartir datos.
Siempre puedes compartir datos usando
NSUserDefaults
. Establezca el valor que desea compartir con respecto a una clave de su elección y obtenga el valorNSUserDefault
asociado a esa clave en el siguiente controlador de vista.[[NSUserDefaults standardUserDefaults] setValue:value forKey:key] [[NSUserDefaults standardUserDefaults] objectForKey:key]
Puede crear una propiedad en
viewcontrollerA
. Cree un objeto deviewcontrollerA
inviewcontrollerB
y asigne el valor deseado a esa propiedad.También puede crear delegados personalizados para esto.
Si desea pasar datos de un controlador a otro, pruebe este código
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];
}
Esta es una respuesta muy antigua y es anti patrón, por favor use delegados. ¡No utilice este enfoque!
1. Cree la instancia del primer controlador de vista en el segundo controlador de vista y establezca su propiedad @property (nonatomic,assign)
.
2. Asigne la SecondviewController
instancia de este controlador de vista.
2. Cuando termine la operación de selección, copie la matriz en el primer controlador de vista. Cuando descargue SecondView, FirstView retendrá los datos de la matriz.
Espero que esto ayude.
Estuve buscando esta solución durante mucho tiempo, Atlast la encontré. En primer lugar, declare todos los objetos en su archivo SecondViewController.h como
@interface SecondViewController: UIviewController
{
NSMutableArray *myAray;
CustomObject *object;
}
Ahora, en su archivo de implementación, asigne la memoria para esos objetos como este
- (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;
}
Ahora ha asignado la memoria para un Array
objeto. ahora puedes llenar esa memoria antes de presionar estoViewController
Vaya a su SecondViewController.hy escriba dos métodos
-(void)setMyArray:(NSArray *)_myArray;
-(void)setMyObject:(CustomObject *)_myObject;
en el archivo de implementación puede implementar la función
-(void)setMyArray:(NSArray *)_myArray
{
[myArra addObjectsFromArray:_myArray];
}
-(void)setMyObject:(CustomObject *)_myObject
{
[object setCustomObject:_myObject];
}
esperando que CustomObject
debe tener una función de setter con él.
ahora su trabajo básico está hecho. vaya al lugar donde desea presionar SecondViewController
y haga lo siguiente
SecondViewController *secondView= [[SecondViewController alloc] initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]] ;
[secondView setMyArray:ArrayToPass];
[secondView setMyObject:objectToPass];
[self.navigationController pushViewController:secondView animated:YES ];
Tenga cuidado con los errores ortográficos.
Esta no es la forma de hacerlo, debe usar delegados, asumiré que tenemos dos controladores de vista ViewController1 y ViewController2 y esta comprobación está en el primero y cuando su estado cambia, desea hacer algo en ViewController2, para lograrlo de la manera adecuada, debe hacer lo siguiente:
Agregue un nuevo archivo a su proyecto (Protocolo Objective-C) Archivo -> Nuevo, ahora asígnele el nombre ViewController1Delegate o lo que quiera y escríbalos entre las directivas @interface y @end
@optional
- (void)checkStateDidChange:(BOOL)checked;
Ahora vaya a ViewController2.hy agregue
#import "ViewController1Delegate.h"
luego cambia su definición a
@interface ViewController2: UIViewController<ViewController1Delegate>
Ahora vaya a ViewController2.m y dentro de la implementación agregue:
- (void)checkStateDidChange:(BOOL)checked {
if (checked) {
// Do whatever you want here
NSLog(@"Checked");
}
else {
// Also do whatever you want here
NSLog(@"Not checked");
}
}
Ahora vaya a ViewController1.hy agregue la siguiente propiedad:
@property (weak, nonatomic) id<ViewController1Delegate> delegate;
Ahora, si está creando ViewController1 dentro de ViewController2 después de algún evento, debe hacerlo de esta manera usando archivos NIB:
ViewController1* controller = [[NSBundle mainBundle] loadNibNamed:@"ViewController1" owner:self options:nil][0];
controller.delegate = self;
[self presentViewController:controller animated:YES completion:nil];
Ahora que está todo listo, siempre que detecte que el evento de verificación cambió en ViewController1, todo lo que tiene que hacer es lo siguiente
[delegate checkStateDidChange:checked]; // You pass here YES or NO based on the check state of your control
Por favor, dígame si hay algo que no esté claro si no entendí su pregunta correctamente.
Si desea enviar datos de un viewController a otro, aquí tiene una forma de hacerlo:
Digamos que tenemos viewControllers: viewControllerA y viewControllerB
Ahora en viewControllerB.h
@interface viewControllerB : UIViewController {
NSString *string;
NSArray *array;
}
- (id)initWithArray:(NSArray)a andString:(NSString)s;
En 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;
}
En 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];
}
Así es como puede pasar datos de viewControllerA a viewControllerB sin configurar ningún delegado. ;)
Sé que este es un tema superado, pero para aquellos que buscan responder a esta pregunta con una inclinación de SWIFT y quieren un ejemplo básico, aquí está mi método para pasar datos si está utilizando un segue para moverse.
Es similar al anterior pero sin los botones, etiquetas y demás. Simplemente pasando datos de una vista a la siguiente.
Configurar el guión gráfico
Hay tres partes.
- El remitente
- El Segue
- El receptor
Este es un diseño de vista muy simple con una transición entre ellos.
Aquí está la configuración para el remitente
Aquí está la configuración del receptor.
Por último, la configuración para el segue.
Los controladores de vista
Mantenemos esto simple para que no haya botones, ni acciones, simplemente estamos moviendo datos del remitente al receptor cuando se carga la aplicación y luego enviamos el valor transmitido a la consola.
Esta página toma el valor cargado inicialmente y lo transmite.
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
}
}
Esta página simplemente envía el valor de la variable a la consola cuando se carga. En este punto, nuestra película favorita debería estar en esa variable.
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)")
}
}
Así es como puede abordarlo si desea usar un segue y no tiene sus páginas bajo un controlador de navegación.
Una vez que se ejecuta, debe cambiar a la vista del receptor automáticamente y pasar el valor del remitente al receptor, mostrando el valor en la consola.
Rápido 5
Bueno, la respuesta de Matt Price está perfectamente bien para pasar datos, pero la voy a reescribir, en la última versión de Swift porque creo que a los nuevos programadores les resulta difícil debido a la nueva sintaxis y métodos / marcos, ya que la publicación original está en Objective-C.
Hay varias opciones para pasar datos entre controladores de vista.
- Uso del mando de navegación Push
- Usando Segue
- Usar delegado
- Uso de Notification Observer
- Usando Block
Voy a reescribir su lógica en Swift con el último marco de iOS.
Pasar datos a través de la inserción del controlador de navegación : de ViewControllerA a ViewControllerB
Paso 1. Declarar variable en ViewControllerB
var isSomethingEnabled = false
Paso 2. Imprimir variable en el método ViewDidLoad de ViewControllerB
override func viewDidLoad() {
super.viewDidLoad()
//Print value received through segue, navigation push
print("Value of 'isSomethingEnabled' from ViewControllerA : ", isSomethingEnabled)
}
Paso 3. En ViewControllerA Pass Data mientras empuja a través del controlador de navegación
if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
viewControllerB.isSomethingEnabled = true
if let navigator = navigationController {
navigator.pushViewController(viewControllerB, animated: true)
}
}
Así que aquí está el código completo para:
ViewControllerA
import UIKit
class ViewControllerA: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK:Passing Data through Navigation PushViewController
@IBAction func goToViewControllerB(_ sender: Any) {
if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
viewControllerB.isSomethingEnabled = true
if let navigator = navigationController {
navigator.pushViewController(viewControllerB, animated: true)
}
}
}
}
ViewControllerB
import UIKit
class ViewControllerB: UIViewController {
//MARK: - Variable for Passing Data through Navigation push
var isSomethingEnabled = false
override func viewDidLoad() {
super.viewDidLoad()
//Print value received through navigation push
print("Value of 'isSomethingEnabled' from ViewControllerA : ", isSomethingEnabled)
}
}
Pasar datos a través de Segue : de ViewControllerA a ViewControllerB
Paso 1. Cree Segue desde ViewControllerA a ViewControllerB y proporcione Identifier = showDetailSegue en Storyboard como se muestra a continuación
Paso 2. En ViewControllerB Declare un isSomethingEnabled viable llamado e imprima su valor.
Paso 3. En ViewControllerA, pase el valor de isSomethingEnabled mientras pasa Segue
Así que aquí está el código completo para:
ViewControllerA
import UIKit
class ViewControllerA: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK: - - Passing Data through Segue - -
@IBAction func goToViewControllerBUsingSegue(_ sender: Any) {
performSegue(withIdentifier: "showDetailSegue", sender: nil)
}
//Segue Delegate Method
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "showDetailSegue") {
let controller = segue.destination as? ViewControllerB
controller?.isSomethingEnabled = true//passing data
}
}
}
ViewControllerB
import UIKit
class ViewControllerB: UIViewController {
var isSomethingEnabled = false
override func viewDidLoad() {
super.viewDidLoad()
//Print value received through segue
print("Value of 'isSomethingEnabled' from ViewControllerA : ", isSomethingEnabled)
}
}
Pasar datos a través de delegado : de ViewControllerB a ViewControllerA
Paso 1. Declare el protocolo ViewControllerBDelegate en el archivo ViewControllerB pero fuera de la clase
protocol ViewControllerBDelegate: NSObjectProtocol {
// Classes that adopt this protocol MUST define
// this method -- and hopefully do something in
// that definition.
func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}
Paso 2. Declarar instancia de variable delegada en ViewControllerB
var delegate: ViewControllerBDelegate?
Paso 3. Enviar datos para delegar dentro del método viewDidLoad de ViewControllerB
delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")
Paso 4. Confirmar ViewControllerBDelegate en ViewControllerA
class ViewControllerA: UIViewController, ViewControllerBDelegate {
// to do
}
Paso 5. Confirme que implementará el delegado en ViewControllerA
if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
viewControllerB.delegate = self//confirming delegate
if let navigator = navigationController {
navigator.pushViewController(viewControllerB, animated: true)
}
}
Paso 6. Implementar el método delegado para recibir datos en ViewControllerA
func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
print("Value from ViewControllerB's Delegate", item!)
}
Así que aquí está el código completo para:
ViewControllerA
import UIKit
class ViewControllerA: UIViewController, ViewControllerBDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
//Delegate method
func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
print("Value from ViewControllerB's Delegate", item!)
}
@IBAction func goToViewControllerForDelegate(_ sender: Any) {
if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
viewControllerB.delegate = self
if let navigator = navigationController {
navigator.pushViewController(viewControllerB, animated: true)
}
}
}
}
ViewControllerB
import UIKit
//Protocol decleare
protocol ViewControllerBDelegate: NSObjectProtocol {
// Classes that adopt this protocol MUST define
// this method -- and hopefully do something in
// that definition.
func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}
class ViewControllerB: UIViewController {
var delegate: ViewControllerBDelegate?
override func viewDidLoad() {
super.viewDidLoad()
//MARK: - - - - Set Data for Passing Data through Delegate - - - - - -
delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")
}
}
Pasar datos a través de Notification Observer : de ViewControllerB a ViewControllerA
Paso 1. Establecer y publicar datos en el observador de notificaciones en ViewControllerB
let objToBeSent = "Test Message from Notification"
NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)
Paso 2. Agregar un observador de notificaciones en ViewControllerA
NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
Paso 3. Recibir el valor de los datos de notificación en ViewControllerA
@objc func methodOfReceivedNotification(notification: Notification) {
print("Value of notification : ", notification.object ?? "")
}
Así que aquí está el código completo para:
ViewControllerA
import UIKit
class ViewControllerA: UIViewController{
override func viewDidLoad() {
super.viewDidLoad()
// add observer in controller(s) where you want to receive data
NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
}
//MARK: Method for receiving Data through Post Notification
@objc func methodOfReceivedNotification(notification: Notification) {
print("Value of notification : ", notification.object ?? "")
}
}
ViewControllerB
import UIKit
class ViewControllerB: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//MARK:Set data for Passing Data through Post Notification
let objToBeSent = "Test Message from Notification"
NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)
}
}
Pasar datos a través del bloque : de ViewControllerB a ViewControllerA
Paso 1. Declarar bloque en ViewControllerB
var AuthorizationCompletionBlock: ((Bool) -> ())? = {_ en}
Paso 2. Establecer datos en bloque en ViewControllerB
if authorizationCompletionBlock != nil
{
authorizationCompletionBlock!(true)
}
Paso 3. Recibir datos de bloque en ViewControllerA
//Receiver Block
controller!.authorizationCompletionBlock = { isGranted in
print("Data received from Block is :", isGranted)
}
Así que aquí está el código completo para:
ViewControllerA
import UIKit
class ViewControllerA: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK:Method for receiving Data through Block
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "showDetailSegue") {
let controller = segue.destination as? ViewControllerB
controller?.isSomethingEnabled = true
//Receiver Block
controller!.authorizationCompletionBlock = { isGranted in
print("Data received from Block is :", isGranted)
}
}
}
}
ViewControllerB
import UIKit
class ViewControllerB: UIViewController {
//MARK:Variable for Passing Data through Block
var authorizationCompletionBlock:((Bool)->())? = {_ in}
override func viewDidLoad() {
super.viewDidLoad()
//MARK:Set data for Passing Data through Block
if authorizationCompletionBlock != nil
{
authorizationCompletionBlock!(true)
}
}
}
Puede encontrar una aplicación de muestra completa en mi GitHub. Por favor, avíseme si tiene alguna pregunta al respecto.
En mi caso, utilicé una clase singleton que puede funcionar como un objeto global que permite acceder a los datos desde casi cualquier lugar de la aplicación. Lo primero es construir una clase singleton. Consulte la página, " ¿Cómo debería verse mi singleton Objective-C? " Y lo que hice para que el objeto fuera accesible globalmente fue simplemente importarlo appName_Prefix.pch
para aplicar la declaración de importación en todas las clases. Para acceder a este objeto y usarlo, simplemente implementé el método de clase para devolver la instancia compartida, que contiene sus propias variables
Pasar datos entre FirstViewController a SecondViewController como se muestra a continuación
Por ejemplo:
Valor de cadena FirstViewController como
StrFirstValue = @"first";
para que podamos pasar este valor en la segunda clase usando el siguiente paso
1> Necesitamos crear un objeto de cadena en el archivo SecondViewController.h
NSString *strValue;
2> Necesidad de declarar la propiedad como se muestra a continuación a continuación en la declaración en el archivo .h
@property (strong, nonatomic) NSString *strSecondValue;
3> Necesita sintetizar ese valor en el archivo FirstViewController.m debajo de la declaración del encabezado
@synthesize strValue;
y en FirstViewController.h:
@property (strong, nonatomic) NSString *strValue;
4> En FirstViewController, desde qué método navegamos a la segunda vista, escriba el código a continuación en ese método.
SecondViewController *secondView= [[SecondViewController alloc]
initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]];
[secondView setStrSecondValue:StrFirstValue];
[self.navigationController pushViewController:secondView animated:YES ];
Actualmente estoy contribuyendo a una solución de código abierto para este problema a través de un proyecto llamado MCViewFactory, que se puede encontrar aquí:
https://github.com/YetiHQ/manticore-iosviewfactory
La idea es imitar el paradigma de intenciones de Android, usando una fábrica global para administrar qué vista estás mirando y usando "intenciones" para cambiar y pasar datos entre vistas. Toda la documentación está en la página de github, pero aquí hay algunos aspectos destacados:
Configura todas sus vistas en archivos .XIB y las registra en el delegado de la aplicación, mientras inicializa la fábrica.
// Register activities
MCViewFactory *factory = [MCViewFactory sharedFactory];
// the following two lines are optional.
[factory registerView:@"YourSectionViewController"];
Ahora, en su VC, cada vez que desee pasar a un nuevo VC y pasar datos, crea una nueva intención y agrega datos a su diccionario (SavedInstanceState). Luego, simplemente configure la intención actual de fábrica:
MCIntent* intent = [MCIntent intentWithSectionName:@"YourSectionViewController"];
[intent setAnimationStyle:UIViewAnimationOptionTransitionFlipFromLeft];
[[intent savedInstanceState] setObject:@"someValue" forKey:@"yourKey"];
[[intent savedInstanceState] setObject:@"anotherValue" forKey:@"anotherKey"];
// ...
[[MCViewModel sharedModel] setCurrentSection:intent];
Todas sus vistas que se ajusten a esto deben ser subclases de MCViewController, que le permiten anular el nuevo método onResume:, lo que le permite acceder a los datos que ha pasado.
-(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];
}
Espero que algunos de ustedes encuentren esta solución útil / interesante.
Cree la propiedad en next view controller .h
y defina getter y setter.
Agregue esto property
en NextVC.h en nextVC
@property (strong, nonatomic) NSString *indexNumber;
Agregar
@synthesize indexNumber;
en NextVC.m
Y última
NextVC *vc=[[NextVC alloc]init];
vc.indexNumber=@"123";
[self.navigationController vc animated:YES];
Hay muchas formas de hacer esto y es importante elegir la correcta. Probablemente una de las decisiones arquitectónicas más importantes radica en cómo se compartirá o se accederá al código del modelo en toda la aplicación.
Escribí una publicación de blog sobre esto hace un tiempo: Compartir código de modelo . He aquí un breve resumen:
Datos compartidos
Un enfoque es compartir punteros a los objetos del modelo entre controladores de vista.
- Iteración de fuerza bruta en los controladores de vista (en Navegación o Controlador de barra de pestañas) para configurar los datos
- Establecer datos en prepareForSegue (si es un guión gráfico) o init (si es programático)
Dado que prepararse para segue es lo más común, aquí hay un ejemplo:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var next = segue.destinationViewController as NextViewController
next.dataSource = dataSource
}
Acceso independiente
Otro enfoque es manejar una pantalla llena de datos a la vez y, en lugar de acoplar los controladores de vista entre sí, acople cada controlador de vista a una única fuente de datos a la que puedan acceder de forma independiente.
La forma más común en que he visto esto es una instancia de singleton . Entonces, si su objeto singleton era DataAccess
, podría hacer lo siguiente en el método viewDidLoad de UIViewController:
func viewDidLoad() {
super.viewDidLoad()
var data = dataAccess.requestData()
}
Hay herramientas adicionales que también ayudan a transmitir datos:
- Observación de valores clave
- NSNotification
- Datos básicos
- NSFetchedResultsController
- Fuente de datos
Datos básicos
Lo bueno de Core Data es que tiene relaciones inversas. Entonces, si solo desea darle a NotesViewController el objeto de notas, puede hacerlo porque tendrá una relación inversa con otra cosa como el cuaderno. Si necesita datos en el cuaderno en NotesViewController, puede realizar una copia de seguridad del gráfico de objetos haciendo lo siguiente:
let notebookName = note.notebook.name
Lea más sobre esto en la publicación de mi blog: Compartir código de modelo
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;
La delegación es la única solución para realizar este tipo de operaciones cuando utiliza archivos .xib; sin embargo, todas las respuestas descritas anteriormente son para storyboard
archivos .xibs que necesita para usar la delegación. esa es la única solución que puedes.
Otra solución es usar el patrón de clase singleton, inicializarlo una vez y usarlo en toda su aplicación.
si desea pasar datos de ViewControlerOne a ViewControllerTwo, pruebe estos ..
hacer esto en ViewControlerOne.h
@property (nonatomic, strong) NSString *str1;
hacer esto en ViewControllerTwo.h
@property (nonatomic, strong) NSString *str2;
Sintetizar str2 en ViewControllerTwo.m
@interface ViewControllerTwo ()
@end
@implementation ViewControllerTwo
@synthesize str2;
hacer esto en ViewControlerOne.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Data or string you wants to pass in ViewControllerTwo..
self.str1 = @"hello world";
}
en los botones haga clic en el evento haga esto ..
-(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];
}
hacer esto en ViewControllerTwo.m
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"%@",str2);
}
Puede guardar datos en el delegado de la aplicación para acceder a ellos a través de los controladores de vista en su aplicación. Todo lo que tiene que hacer es crear una instancia compartida de delegado de la aplicación
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
Por ejemplo
si declara un NSArray object *arrayXYZ
, puede acceder a él en cualquier controlador de vista medianteappDelegate.arrayXYZ
Si desea enviar datos de un viewController a otro, aquí tiene una forma de hacerlo:
Digamos que tenemos viewControllers: ViewController y NewViewController.
en 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
en 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];
}
En NewViewController.h
#import <UIKit/UIKit.h>
@interface NewViewController : UITableViewController
{
NSArray *arrayList;
NSString *name,*age,*dob,*mobile;
}
@property(nonatomic, retain)NSArray *arrayList;
@end
En 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
De esta manera podemos pasar los datos de un controlador de vista a otro controlador de vista ...
Me gusta la idea de objetos modelo y objetos simulados basados en NSProxy para confirmar o descartar datos si lo que el usuario selecciona se puede cancelar.
Es fácil pasar datos ya que es un solo objeto o un par de objetos y si tiene, digamos, un controlador UINavigationController, puede mantener la referencia al modelo dentro y todos los controladores de vista presionados pueden acceder a él directamente desde el controlador de navegación.
He visto a mucha gente complicar esto usando el didSelectRowAtPath
método. Estoy usando Core Data en mi ejemplo.
- (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 líneas de código dentro del método y listo.
Hay muchas respuestas a estas preguntas que ofrecen muchas formas diferentes de realizar la comunicación del controlador de vista que de hecho funcionarían, pero no veo en ninguna parte mencionada cuál es la mejor para usar y cuáles evitar.
En la práctica, en mi opinión, solo se recomiendan algunas soluciones:
- Para transmitir datos:
- anular el
prepare(for:sender:)
método deUIViewController
cuando se usa un guión gráfico y segues - pasar datos a través de un inicializador o propiedades al realizar transiciones del controlador de vista a través del código
- anular el
- Pasar datos al revés
- actualice el estado compartido de la aplicación (que puede pasar entre los controladores de vista con cualquiera de los métodos anteriores)
- usar delegación
- utilizar una secuencia de relajación
Soluciones que recomiendo NO usar:
- Hacer referencia al controlador anterior directamente en lugar de usar la delegación
- Compartir datos a través de un singleton
- Pasar datos a través del delegado de la aplicación
- Compartir datos a través de los valores predeterminados del usuario
- Pasar datos a través de notificaciones
Estas soluciones, aunque funcionan a corto plazo, introducen demasiadas dependencias que alterarán la arquitectura de la aplicación y crearán más problemas más adelante.
Para los interesados, escribí algunos artículos que abordan estos puntos con más profundidad y destacan los diversos inconvenientes:
- Cómo se comunican los controladores de vista de iOS entre sí
- Cómo estructurar el código de las aplicaciones iOS
- Comprensión de los principios arquitectónicos básicos del desarrollo de iOS con un ejemplo práctico