ios - Pasando datos entre los controladores de vista




objective-c oop (20)

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

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

Mi pregunta es, ¿cómo transfiero los datos de una vista a otra? UITableView las selecciones en el UITableView en una matriz, pero, ¿cómo lo UITableView a la vista del formulario de ingreso de datos anterior para que pueda guardarse junto con los demás datos a los Datos del Núcleo al enviar el formulario?

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

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


Rápido

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

Pasando los datos al siguiente View Controller

El siguiente es un ejemplo basado en el video. La idea es pasar una cadena desde el campo de texto en el primer controlador de vista a la etiqueta en el segundo controlador de vista.

Crea el diseño del guión gráfico en el Interface Builder. Para hacer el segue, solo tienes que presionar Control y hacer clic en el botón y arrastrar hasta el Controlador de la segunda vista.

Primer controlador de vista

El código del First View Controller es

import UIKit

class FirstViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    // This function is called before the segue
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        // get a reference to the second view controller
        let secondViewController = segue.destination as! SecondViewController

        // set a variable in the second view controller with the String to pass
        secondViewController.receivedString = textField.text!
    }

}

Controlador de la 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 te olvides

  • UITextField los puntos de venta de la UITextField y la UILabel .
  • Establezca el primer y segundo Controladores de vista en los archivos Swift correspondientes en IB.

Pasando datos de vuelta al controlador de vista anterior

Para pasar los datos del segundo controlador de vista al primer controlador de vista, utiliza un protocolo y un delegado . Este video es un paseo muy claro a pesar de ese proceso:

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

Crea el diseño del guión gráfico en el Interface Builder. Nuevamente, para hacer el segue, solo tienes que controlar el arrastre desde el botón hasta el controlador de la segunda vista. Establezca el identificador de segue en showSecondViewController . Además, no olvide conectar los puntos de venta y las acciones con los nombres en el siguiente código.

Primer controlador de vista

El código del First View Controller es

import UIKit

class FirstViewController: UIViewController, DataEnteredDelegate {

    @IBOutlet weak var label: UILabel!

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showSecondViewController" {
            let secondViewController = segue.destination as! SecondViewController
            secondViewController.delegate = self
        }
    }

    func userDidEnterInformation(info: String) {
        label.text = info
    }
}

Tenga en cuenta el uso de nuestro protocolo personalizado DataEnteredDelegate .

Segundo controlador y protocolo de vista

El código para el segundo controlador de vista es

import UIKit

// protocol used for sending data back
protocol DataEnteredDelegate: class {
    func userDidEnterInformation(info: String)
}

class SecondViewController: UIViewController {

    // making this a weak variable so that it won't create a strong reference cycle
    weak var delegate: DataEnteredDelegate? = nil

    @IBOutlet weak var textField: UITextField!

    @IBAction func sendTextBackButton(sender: AnyObject) {

        // call this method on whichever class implements our delegate protocol
        delegate?.userDidEnterInformation(info: textField.text!)

        // go back to the previous view controller
        _ = self.navigationController?.popViewController(animated: true)
    }
}

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

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


Después de más investigaciones, parecía que los Protocolos y los Delegados eran la forma correcta / Apple prefería hacerlo.

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 vistas.

Gracias por toda tu ayuda


Esta pregunta parece ser muy popular aquí en , 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.

Pasando los datos hacia adelante

Pasar datos a un controlador de vista desde otro controlador de vista. Utilizaría este método si quisiera pasar un objeto / valor de un controlador de vista a otro controlador de vista que pueda estar empujando a una pila de navegación.

Para este ejemplo tendremos ViewControllerA y ViewControllerB

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

  1. en ViewControllerB.h crea una propiedad para el BOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. en ViewControllerA debe informarle sobre ViewControllerB así que use un

    #import "ViewControllerB.h"
    

    Entonces donde quieres cargar la vista por ejemplo. didSelectRowAtIndex o algún IBAction debe establecer la propiedad en ViewControllerB antes de empujarlo 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 en ViewControllerB en BOOL valor YES .

Pasando datos hacia adelante utilizando Segues

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

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

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

  1. en ViewControllerB.h crea una propiedad para el BOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. en ViewControllerA debe informarle sobre ViewControllerB así que use un

    #import "ViewControllerB.h"
    
  3. Cree un segmento de ViewControllerA a ViewControllerB en el guión gráfico y "showDetailSegue" 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 algún segue, por lo que necesitamos detectar a qué segue se llamó y luego hacer algo. En nuestro ejemplo, buscaremos "showDetailSegue" y si eso se realiza, pasaremos nuestro valor BOOL a ViewControllerB

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

    Si tiene sus vistas incrustadas en un controlador de navegación, debe cambiar el método anterior ligeramente al 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 en BOOL valor YES .

Pasando datos de vuelta

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

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

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

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

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

    @property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
    
  3. En ViewControllerB llamamos a un mensaje en el delegate cuando abrimos el controlador de vista.

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

    #import "ViewControllerB.h"
    
    @interface ViewControllerA : UIViewController <ViewControllerBDelegate>
    
  5. 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);
    }
    
  6. Antes de empujar viewControllerB a la pila de navegación, debemos decirle a ViewControllerB que ViewControllerA es su delegado, de lo contrario obtendremos un error.

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

Referencias

  1. Uso de la delegación para comunicarse con otros controladores de visualización en la Guía de programación de View Controller
  2. Patrón de delegado

NSNotification center es otra forma de pasar 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 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 bloque (escucha) donde necesita valor (por ejemplo, necesita su respuesta API en ControllerA o necesita datos 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 de trabajo para bloques


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

La pregunta se refiere a pasar información entre los controladores de vista. El ejemplo específico dado pregunta sobre el paso de información entre vistas, pero dada la novedad auto-declarada de iOS, el cartel original probablemente significó entre viewControllers, no entre vistas (sin ninguna participación de ViewControllers). Parece que todas las respuestas se centran en dos controladores de vista, pero ¿qué sucede si la aplicación evoluciona y necesita involucrar a más de dos controladores 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 ser respondidas.

Para ayudar a cualquier persona que vea esta pregunta, quien quiera una respuesta completa, voy a intentar proporcionarla.

Escenarios de aplicación

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

Escenario uno: 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 necesitan 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 nuevo formulario de artículo

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 va a ViewControllerA (Artículos de lujo) y también a ViewControllerC (Inventario de todo el hogar), pero no cuando el usuario va a ViewControllerB (Artículos no asegurados). Tenemos que preocuparnos no solo por agregar nuevos elementos, sino también por eliminar elementos (que pueden estar permitidos en cualquiera de los cuatro controladores de vista), o editar elementos existentes (que pueden permitirse desde el "Formulario de Agregar nuevo elemento", reutilizando los mismos para la edición).

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 un solo 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 tenemos 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 visualización cada vez que un controlador de vista realizó un cambio?

Las soluciones: delegados y el patrón observador, y Singletons

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

  • segues
  • delegados
  • Configuración de propiedades en controladores de vista directamente
  • NSUserDefaults (en realidad una mala elección)

En el escenario dos, tenemos otras soluciones viables:

  • Patrón observador
  • Singletons

Un singleton es una instancia de una clase, siendo esa instancia la única instancia en existencia durante su vida útil. Un singleton recibe su nombre del hecho de que es la única instancia. 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 de observador. El patrón de observador se utiliza para que un objeto responda a los cambios realizados por otro objeto. En el segundo escenario, tenemos cuatro controladores de vista diferentes, todos los cuales desean conocer los cambios en los datos subyacentes. Los "datos subyacentes" deben pertenecer a una sola instancia, un singleton. El "conocimiento de los cambios" se logra observando los cambios realizados en el singleton.

La aplicación de inventario doméstico tendría una única instancia de una clase que está diseñada para administrar una lista de artículos de inventario. El gerente manejarí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 se modifica la colección de artículos del inventario doméstico, los controladores de vista deben conocer este cambio. La definición de clase anterior no hace obvio cómo sucederá esto. Necesitamos seguir el patrón observador. Los controladores de vista deben observar formalmente el SharedManager. Hay dos formas de observar otro objeto:

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

En el escenario dos, no tenemos una propiedad única 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 utilizando NSNotificationCenter. Cada uno de los cuatro controladores de vista se suscribiría a las notificaciones, y el SharedManager enviaría notificaciones al centro de notificaciones cuando sea apropiado. El administrador de inventario no necesita saber nada sobre los controladores de vista o las instancias de cualquier otra clase que pueda estar interesada en saber cuándo cambia la colección de artículos de inventario; El 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 las notificaciones.

Muchos programadores principiantes aprovechan el hecho de que siempre hay exactamente un Delegado de la Aplicación en el tiempo de vida de la aplicación, que es accesible globalmente. Los programadores principiantes utilizan este hecho para incluir objetos y funcionalidades en la aplicación. El hecho de que AppDelegate sea un singleton no significa que deba reemplazar a todos los demás singletons. Esta es una mala práctica ya que supone una carga excesiva para una clase, rompiendo las buenas prácticas orientadas a objetos. Cada clase debe tener un rol claro que se explica 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, el Core Data Stack no debe dejarse en el AppDelegate, sino que debe colocarse en su propia clase, una clase coreDataManager.

Referencias


La M en MVC es para "Modelo" y en el paradigma MVC, el rol 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 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 que serlo, el modelo para su aplicación podría ser tan simple como una serie de cadenas o diccionarios.

El rol 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 variedad de diccionarios, donde 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 un botón y su controlador crea un nuevo diccionario (mutable) y lo agrega a la matriz. Para completar la fila, el controlador crea un controlador de vista de detalle y le da el nuevo diccionario. El controlador de vista de detalle rellena el diccionario y lo devuelve. El diccionario ya es parte del modelo, por lo que no es necesario que ocurra nada más.


Pasar los datos de vuelta desde ViewController 2 (destino) a viewController 1 (Source) es lo más interesante. Suponiendo que uses storyBoard, estas son todas las formas en que me enteré:

  • Delegar
  • Notificación
  • Valores predeterminados del usuario
  • Semifallo

Aquellos ya fueron discutidos aquí.

Encontré que hay más formas:

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

-Utilizar storyboards desenrollar (salir)

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

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

En el storyBoard, enganche el botón "regresar" al botón verde Salir (Desenrollar) de la vc. Ahora tiene un segmento que "vuelve" para que pueda usar la propiedad destinationViewController en el prepareForSegue de VC2 y cambiar cualquier propiedad de VC1 antes de que regrese.

  • Otra opción de usar 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;
    } 
    

    Y en el 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 el prepareForSegue.

Espero haber agregado algo a la discusión.

:) Saludos.


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

NoticiasDetailViewController.h

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

NoticiasDetailViewController.m

@synthesize newsHeadlineStr;

Pasar datos entre FirstViewController a SecondViewController como se muestra a continuación

Por ejemplo:

El valor de la cadena FirstViewController como

StrFirstValue = @"first";

para que podamos pasar este valor en 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 debajo de la declaración en el archivo .h

@property (strong, nonatomic)  NSString *strSecondValue;

3> Es necesario 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 ];

El OP no mencionó los controladores de vista, pero sí muchas de las respuestas, por lo que quise acompañar a algunas de las nuevas características del LLVM para que esto sea más fácil cuando se desea pasar datos de un controlador de vista a otro y obteniendo algunos resultados de vuelta.

Segues del guión gráfico, los bloques ARC y LLVM hacen esto más fácil que nunca para mí. Algunas de las respuestas mencionadas mencionan los guiones gráficos y siguen, pero aún dependen 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 los punteros de paso a cosas derivadas de NSObjects sean simples, por lo tanto, si desea que el controlador subordinado agregue / cambie / modifique algunos datos para usted, páselo a una instancia mutable. Los bloques facilitan las acciones de paso, por lo que si desea que el controlador subordinado invoque una acción en su controlador de nivel superior, pásele un bloque. Usted define el bloque para aceptar cualquier número de argumentos que tenga sentido para usted. También puedes diseñar la API para usar múltiples bloques si eso se adapta mejor a las cosas.

Aquí hay dos ejemplos triviales del pegamento segue. El primero es directo y muestra un parámetro pasado para la entrada, el segundo para la 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 cómo pasar 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];
    }];
}

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, debes hacer lo siguiente:

Agregue un nuevo archivo a su proyecto (Protocolo Objective-C) Archivo -> Nuevo, ahora llámelo ViewController1Delegate o lo que quiera y escríbalos entre las directivas @interface y @end

@optional

- (void)checkStateDidChange:(BOOL)checked;

Ahora ve a ViewController2.h y agrega

#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 ve a ViewController1.h y agrega la siguiente propiedad:

@property (weak, nonatomic) id<ViewController1Delegate> delegate; 

Ahora, si está creando ViewController1 dentro de ViewController2 después de algún evento, debería hacerlo de esta manera utilizando archivos NIB:

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

Ahora está todo listo, siempre que detecte el evento de verificación cambiado 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.


Hay muchas respuestas a estas preguntas que ofrecen muchas formas diferentes de realizar una comunicación con el controlador de vista que de hecho funcionaría, pero no veo en ningún lado cuáles son las mejores para usar y cuáles para evitar.

En la práctica, en mi opinión solo se recomiendan algunas soluciones:

  • Para pasar los datos hacia adelante:
    • Anula el prepare(for:sender:)método de UIViewControllercuando se utiliza un guión gráfico y segues
    • pase los datos a través de un inicializador o de las propiedades cuando realice ver las transiciones del controlador a través del código
  • Pasar datos hacia atrás.
    • actualizar 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 un segue desenrollar

Soluciones que recomiendo NO usar:

  • Referencia al controlador anterior directamente en lugar de usar delegación
  • Compartiendo datos a través de un singleton
  • Pasando datos a través del delegado de la aplicación.
  • Compartir datos a través de los valores por defecto del usuario
  • Pasar datos a través de notificaciones.

Estas soluciones, aunque funcionan a corto plazo, introducen demasiadas dependencias que distorsionarán la arquitectura de la aplicación y crearán más problemas posteriormente.

Para aquellos interesados, escribí algunos artículos que tratan estos puntos con mayor profundidad y resaltan los diversos inconvenientes:


Me gusta la idea de modelar objetos y simular objetos basados ​​en NSProxy para confirmar o descartar datos si se puede cancelar lo que seleccione el usuario.

Es fácil pasar los datos ya que es un objeto único o un par de objetos y, si tiene el controlador UINavigationController, puede mantener la referencia al modelo interior y todos los controladores de vista empujados pueden acceder directamente desde el controlador de navegación.


Si desea enviar datos de uno a otro viewController, 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 ...


Si desea enviar datos de uno a otro viewController, 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];

}

Entonces, así es como puede pasar los datos de viewControllerA a viewControllerB sin configurar ningún delegado. ;)


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

Crea la propiedad en siguiente view controller .hy define getter y setter.

Agregue esto propertyen NextVC.h en nextVC

@property (strong, nonatomic) NSString *indexNumber;

Añadir

@synthesize indexNumber; en NextVC.m

Y última

NextVC *vc=[[NextVC alloc]init];

[email protected]"123";

[self.navigationController vc animated:YES];

En mi caso, utilicé una clase de 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 ser mi Singleton de Objective-C? " Y lo que hice para hacer que el objeto sea accesible globalmente fue simplemente importarlo, lo appName_Prefix.pchcual es para aplicar una declaración de importación en cada clase. Para acceder a este objeto y usarlo, simplemente implementé el método de clase para devolver la instancia compartida, que contiene sus propias variables


Este es un tutorial realmente bueno para cualquiera que quiera uno. Aquí está el código de ejemplo:

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

Hay muchas maneras 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 accederá el código del modelo a través de la aplicación.

Hace un tiempo escribí una publicación en el blog sobre esto: Compartir código de modelo . Aquí hay un breve resumen:

Datos compartidos

Un enfoque es compartir punteros a los objetos modelo entre controladores de vista.

  • La iteración de fuerza bruta en los controladores de vista (en Navegación o Controlador de la Barra de pestañas) para configurar los datos
  • Establecer datos en prepareForSegue (si son guiones gráficos) o init (si es programático)

Como prepararse para segue es lo más común aquí es 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í, acoplar cada controlador de vista a una única fuente de datos a la que puedan acceder de forma independiente.

La forma más común que he visto hacer esto es una instancia de singleton . Entonces, si su objeto singleton fuera DataAccess, podría hacer lo siguiente en el método viewDidLoad de UIViewController:

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

Hay herramientas de adición que también ayudan a transmitir datos:

  • Observación del valor clave
  • NSNotification
  • Datos básicos
  • NSFetchedResultsController
  • Fuente de datos

Datos básicos

Lo bueno de Core Data es que tiene relaciones inversas. Por lo tanto, si solo quiere darle a NotesViewController el objeto de notas, puede tener una relación inversa con otra cosa, como el cuaderno. Si necesita datos en el cuaderno en el NotesViewController, puede hacer una copia de seguridad del gráfico de objetos haciendo lo siguiente:

let notebookName = note.notebook.name

Lea más sobre esto en mi blog: Compartir código de modelo


He visto a mucha gente complicar esto usando el didSelectRowAtPathmé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.





uiviewcontroller