profesional pasar para otro mega entre descargar datos curso completo cero apple ios swift

ios - pasar - swift para windows



¿Cómo comparte datos entre los controladores de vista y otros objetos en Swift? (8)

Digamos que tengo múltiples controladores de vista en mi aplicación Swift y quiero poder pasar datos entre ellos. Si estoy varios niveles abajo en una pila de controlador de vista, ¿cómo paso los datos a otro controlador de vista? ¿O entre pestañas en un controlador de vista de barra de pestañas?

(Tenga en cuenta que esta pregunta es un "timbre"). Se le pregunta tanto que decidí escribir un tutorial sobre el tema. Vea mi respuesta a continuación.


Como @nhgrif señaló en su excelente respuesta, hay muchas formas diferentes en que los VC (controladores de vista) y otros objetos pueden comunicarse entre sí.

El singleton de datos que describí en mi primera respuesta es realmente más sobre compartir y guardar el estado global que sobre comunicarse directamente.

La respuesta de nhrif le permite enviar información directamente desde el origen al VC de destino. Como mencioné en la respuesta, también es posible enviar mensajes desde el destino a la fuente.

De hecho, puede configurar un canal activo de una o dos vías entre diferentes controladores de vista. Si los controladores de vista están vinculados a través de un guión gráfico, el tiempo para configurar los enlaces está en el método prepareFor Segue.

Tengo un proyecto de muestra en Github que usa un controlador de vista principal para alojar 2 vistas de tabla diferentes como elementos secundarios. Los controladores de vista secundarios están vinculados mediante segmentos de inserción, y el controlador de vista principal conecta los enlaces bidireccionales con cada controlador de vista en el método prepareForSegue.

Puede encontrar ese proyecto en github (enlace). Sin embargo, lo escribí en Objective-C y no lo he convertido a Swift, por lo que si no te sientes cómodo en Objective-C, podría ser un poco difícil de seguir.


Depende de cuándo quieres obtener datos.

Si desea obtener datos cuando lo desee, puede usar un patrón singleton. La clase de patrón está activa durante el tiempo de ejecución de la aplicación. Aquí hay un ejemplo del patrón singleton.

class AppSession: NSObject { static let shared = SessionManager() var username = "Duncan" } class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() print(AppSession.shared.username) } }

Si desea obtener datos después de cualquier acción, puede usar NotificationCenter.

extension Notification.Name { static let loggedOut = Notification.Name("loggedOut") } @IBAction func logoutAction(_ sender: Any) { NotificationCenter.default.post(name: .loggedOut, object: nil) } NotificationCenter.default.addObserver(forName: .loggedOut, object: nil, queue: OperationQueue.main) { (notify) in print("User logged out") }


En lugar de crear un controlador de datos singelton, sugeriría crear una instancia de controlador de datos y pasarla. Para admitir la inyección de dependencia, primero crearía un protocolo DataController :

protocol DataController { var someInt : Int {get set} var someString : String {get set} }

Luego crearía una clase SpecificDataController (o cualquier nombre que sea apropiado actualmente):

class SpecificDataController : DataController { var someInt : Int = 5 var someString : String = "Hello data" }

La clase ViewController debería tener un campo para contener el dataController . Observe que el tipo de dataController es el protocolo DataController . De esta manera, es fácil cambiar las implementaciones del controlador de datos:

class ViewController : UIViewController { var dataController : DataController? ... }

En AppDelegate podemos establecer el control de datos dataController :

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { if let viewController = self.window?.rootViewController as? ViewController { viewController.dataController = SpecificDataController() } return true }

Cuando nos movemos a un viewController diferente, podemos pasar el dataController en:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { ... }

Ahora, cuando deseamos cambiar el controlador de datos para una tarea diferente, podemos hacer esto en AppDelegate y no tenemos que cambiar ningún otro código que use el controlador de datos.

Esto es, por supuesto, excesivo si simplemente queremos pasar un solo valor. En este caso, es mejor ir con la respuesta de nhgrif.

Con este enfoque podemos separar la vista de la parte lógica.


Esta pregunta surge todo el tiempo.

Una sugerencia es crear un contenedor de datos singleton: un objeto que se crea una vez y solo una vez en la vida de su aplicación, y persiste durante la vida de su aplicación.

Este enfoque es adecuado para una situación en la que tiene datos de aplicaciones globales que deben estar disponibles / modificables en diferentes clases en su aplicación.

Otros enfoques, como la configuración de enlaces unidireccionales o bidireccionales entre los controladores de vista, se adaptan mejor a situaciones en las que pasa información / mensajes directamente entre los controladores de vista.

(Ver la respuesta de nhgrif, a continuación, para otras alternativas).

Con un contenedor de datos singleton, agrega una propiedad a su clase que almacena una referencia a su singleton, y luego usa esa propiedad cada vez que necesita acceso.

Puede configurar su singleton para que guarde su contenido en el disco para que el estado de su aplicación persista entre lanzamientos.

Creé un proyecto de demostración en GitHub que demuestra cómo puedes hacer esto. Aqui esta el link:

Proyecto SwiftDataContainerSingleton en GitHub Aquí está el archivo README de ese proyecto:

SwiftDataContainerSingleton

Una demostración del uso de un contenedor de datos singleton para guardar el estado de la aplicación y compartirla entre objetos.

La clase DataContainerSingleton es el singleton real.

Utiliza una constante estática sharedDataContainer para guardar una referencia al singleton.

Para acceder al singleton, use la sintaxis

DataContainerSingleton.sharedDataContainer

El proyecto de muestra define 3 propiedades en el contenedor de datos:

var someString: String? var someOtherString: String? var someInt: Int?

Para cargar la propiedad someInt del contenedor de datos, usaría un código como este:

let theInt = DataContainerSingleton.sharedDataContainer.someInt

Para guardar un valor en someInt, usaría la sintaxis:

DataContainerSingleton.sharedDataContainer.someInt = 3

El método init de DataContainerSingleton agrega un observador para UIApplicationDidEnterBackgroundNotification . Ese código se ve así:

goToBackgroundObserver = NSNotificationCenter.defaultCenter().addObserverForName( UIApplicationDidEnterBackgroundNotification, object: nil, queue: nil) { (note: NSNotification!) -> Void in let defaults = NSUserDefaults.standardUserDefaults() //----------------------------------------------------------------------------- //This code saves the singleton''s properties to NSUserDefaults. //edit this code to save your custom properties defaults.setObject( self.someString, forKey: DefaultsKeys.someString) defaults.setObject( self.someOtherString, forKey: DefaultsKeys.someOtherString) defaults.setObject( self.someInt, forKey: DefaultsKeys.someInt) //----------------------------------------------------------------------------- //Tell NSUserDefaults to save to disk now. defaults.synchronize() }

En el código del observador, guarda las propiedades del contenedor de datos en NSUserDefaults . También puede usar NSCoding , Core Data u otros métodos para guardar datos de estado.

El método init de DataContainerSingleton también intenta cargar valores guardados para sus propiedades.

Esa parte del método init se ve así:

let defaults = NSUserDefaults.standardUserDefaults() //----------------------------------------------------------------------------- //This code reads the singleton''s properties from NSUserDefaults. //edit this code to load your custom properties someString = defaults.objectForKey(DefaultsKeys.someString) as! String? someOtherString = defaults.objectForKey(DefaultsKeys.someOtherString) as! String? someInt = defaults.objectForKey(DefaultsKeys.someInt) as! Int? //-----------------------------------------------------------------------------

Las claves para cargar y guardar valores en NSUserDefaults se almacenan como constantes de cadena que forman parte de una estructura DefaultsKeys , definida de esta manera:

struct DefaultsKeys { static let someString = "someString" static let someOtherString = "someOtherString" static let someInt = "someInt" }

Hace referencia a una de estas constantes como esta:

DefaultsKeys.someInt

Usando el contenedor de datos singleton:

Esta aplicación de muestra hace un uso trival del contenedor de datos singleton.

Hay dos controladores de vista. La primera es una subclase personalizada de UIViewController ViewController , y la segunda es una subclase personalizada de UIViewController SecondVC .

Ambos controladores de vista tienen un campo de texto en ellos, y ambos cargan un valor de la propiedad someInt del contenedor de datos singlelton en el campo de texto en su método viewWillAppear , y ambos guardan el valor actual del campo de texto nuevamente en ''someInt'' de los datos envase.

El código para cargar el valor en el campo de texto está en el método viewWillAppear: :

override func viewWillAppear(animated: Bool) { //Load the value "someInt" from our shared ata container singleton let value = DataContainerSingleton.sharedDataContainer.someInt ?? 0 //Install the value into the text field. textField.text = "/(value)" }

El código para guardar el valor editado por el usuario en el contenedor de datos está en los métodos textFieldShouldEndEditing los controladores de vista:

func textFieldShouldEndEditing(textField: UITextField) -> Bool { //Save the changed value back to our data container singleton DataContainerSingleton.sharedDataContainer.someInt = textField.text!.toInt() return true }

Debe cargar valores en su interfaz de usuario en viewWillAppear en lugar de viewDidLoad para que su IU se actualice cada vez que se muestre el controlador de vista.


Otra alternativa es utilizar el centro de notificaciones (NSNotificationCenter) y publicar notificaciones. Ese es un acoplamiento muy flojo. El remitente de una notificación no necesita saber o importarle quién está escuchando. Simplemente publica una notificación y se olvida de ella.

Las notificaciones son buenas para el paso de mensajes de uno a muchos, ya que puede haber un número arbitrario de observadores que escuchan un mensaje dado.


SWIFT 3:

Si tiene un guión gráfico con segmentos identificados, use:

func prepare(for segue: UIStoryboardSegue, sender: Any?)

Aunque si hace todo programáticamente, incluida la navegación entre diferentes UIViewControllers, use el método:

func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool)

Nota: para usar la segunda forma que necesita para hacer su UINavigationController, está presionando UIViewControllers, un delegado y debe ajustarse al protocolo UINavigationControllerDelegate:

class MyNavigationController: UINavigationController, UINavigationControllerDelegate { override func viewDidLoad() { self.delegate = self } func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { // do what ever you need before going to the next UIViewController or back //this method will be always called when you are pushing or popping the ViewController } }


Tu pregunta es muy amplia. Sugerir que hay una solución simple para todos los escenarios es un poco ingenuo. Entonces, veamos algunos de estos escenarios.

El escenario más común sobre en mi experiencia es la simple información de paso de un controlador de vista al siguiente.

Si usamos storyboard, nuestro primer controlador de vista puede anular prepareForSegue , que es exactamente para lo que está allí. Se UIStoryboardSegue objeto UIStoryboardSegue cuando se llama a este método, y contiene una referencia a nuestro controlador de vista de destino. Aquí, podemos establecer los valores que queremos pasar.

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if segue.identifier == "MySegueID" { if let destination = segue.destinationViewController as? SecondController { destination.myInformation = self.myInformation } } }

Alternativamente, si no estamos usando guiones gráficos, estamos cargando nuestro controlador de vista desde una punta. Nuestro código es un poco más simple entonces.

func showNextController() { let destination = SecondController(nibName: "SecondController", bundle: NSBundle.mainBundle()) destination.myInformation = self.myInformation self.showViewController(destination, sender: self) }

En ambos casos, myInformation es una propiedad en cada controlador de vista que contiene los datos que se deben pasar de un controlador de vista al siguiente. Obviamente no tienen que tener el mismo nombre en cada controlador.

También podríamos querer compartir información entre pestañas en un UITabBarController .

En este caso, en realidad es potencialmente incluso más simple.

Primero, UITabBarController una subclase de UITabBarController y UITabBarController propiedades para cualquier información que queramos compartir entre las distintas pestañas:

class MyCustomTabController: UITabBarController { var myInformation: [String: AnyObject]? }

Ahora, si estamos construyendo nuestra aplicación desde el guión gráfico, simplemente cambiamos la clase del controlador de nuestra barra de pestañas del UITabBarController predeterminado a MyCustomTabController . Si no estamos usando un guión gráfico, simplemente instanciamos una instancia de esta clase personalizada en lugar de la clase predeterminada UITabBarController y agregamos nuestro controlador de vista a esto.

Ahora, todos nuestros controladores de vista dentro del controlador de la barra de pestañas pueden acceder a esta propiedad como tal:

if let tbc = self.tabBarController as? MyCustomTabController { // do something with tbc.myInformation }

Y al subclasificar UINavigationController de la misma manera, podemos adoptar el mismo enfoque para compartir datos en toda una pila de navegación:

if let nc = self.navigationController as? MyCustomNavController { // do something with nc.myInformation }

Hay varios otros escenarios. De ninguna manera esta respuesta los cubre a todos.


Swift 4

Hay tantos enfoques para el paso de datos en forma rápida. Aquí estoy agregando algunos de los mejores enfoques.

1) Usando StoryBoard Segue

Los segmentos de guión gráfico son muy útiles para pasar datos entre los controladores de vista de origen y destino y viceversa.

// If you want to pass data from ViewControllerB to ViewControllerA while user tap on back button of ViewControllerB. @IBAction func unWindSeague (_ sender : UIStoryboardSegue) { if sender.source is ViewControllerB { if let _ = sender.source as? ViewControllerB { self.textLabel.text = "Came from B = B->A , B exited" } } } // If you want to send data from ViewControllerA to ViewControllerB override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.destination is ViewControllerB { if let vc = segue.destination as? ViewControllerB { vc.dataStr = "Comming from A View Controller" } } }

2) Uso de métodos delegados

ViewControllerD

//Make the Delegate protocol in Child View Controller (Make the protocol in Class from You want to Send Data) protocol SendDataFromDelegate { func sendData(data : String) } import UIKit class ViewControllerD: UIViewController { @IBOutlet weak var textLabelD: UILabel! var delegate : SendDataFromDelegate? //Create Delegate Variable for Registering it to pass the data override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. textLabelD.text = "Child View Controller" } @IBAction func btnDismissTapped (_ sender : UIButton) { textLabelD.text = "Data Sent Successfully to View Controller C using Delegate Approach" self.delegate?.sendData(data:textLabelD.text! ) _ = self.dismiss(animated: true, completion:nil) } }

ViewControllerC

import UIKit class ViewControllerC: UIViewController , SendDataFromDelegate { @IBOutlet weak var textLabelC: UILabel! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } @IBAction func btnPushToViewControllerDTapped( _ sender : UIButton) { if let vcD = self.storyboard?.instantiateViewController(withIdentifier: "ViewControllerD") as? ViewControllerD { vcD.delegate = self // Registring Delegate (When View Conteoller D gets Dismiss It can call sendData method // vcD.textLabelD.text = "This is Data Passing by Referenceing View Controller D Text Label." //Data Passing Between View Controllers using Data Passing self.present(vcD, animated: true, completion: nil) } } //This Method will called when when viewcontrollerD will dismiss. (You can also say it is a implementation of Protocol Method) func sendData(data: String) { self.textLabelC.text = data } }