swift2 - Variable global y enlace opcional en Swift
ios9 optional-variables (1)
Comenzaré repitiendo mi comentario de arriba.
Es posible que hayas malinterpretado el concepto de variables globales en Swift.
Si tiene una variable global, no tendrá que "pasarla" entre ninguna vista / método / clase, etc., porque la variable se define en el alcance global (accesible en todas partes).
Generalmente, las variables globales no son una buena idea, y es algo que desea evitar.
En cuanto a la cuestión de las variables globales y rápida, realmente debería incluir singletons en la discusión. Ver, por ejemplo, los siguientes hilos SO existentes:
Comunicación entre TableViewController y ViewController por medio de segues (preparación y desenrollado de los segmentos)
(Esta respuesta terminó siendo muy, y probablemente demasiado minuciosa, ya que no sabía en detalle cuál es su estado actual del programa tableview / viewcontroller. Disculpe la respuesta prolongada y las molestias que esto pueda ocasionar a los lectores) .
Ahora, dejemos las variables globales y analicemos una (entre otras) opciones viables para la comunicación entre los dos controladores en su ejemplo. De su pregunta, resumiré su ejemplo de la siguiente manera
- VC1 : punto de entrada del guión gráfico, un
UITableViewController
consiste enUITableViewCell
s, donde, en estas celdas, se muestra texto, por ejemplo, a través de instancias deUILabel
. - VC2 : un
UIViewController
, accesible desde las celdas de VC1, que contiene una instancia deUITextField
. Cuando el usuario ingresa texto en este campo de texto, desea que el texto se muestre en la celda asociada en VC2 (asociado en el sentido de que era la celda en VC1 que se utilizó para acceder a VC2).
TableViewController
VC1 y VC2 con las clases (toque de cocoa) TableViewController
( TableViewController.swift ) y ViewController
( ViewController.swift ), respectivamente. Las celdas en el controlador de vista de tabla se asociarán con la clase ( vista de cacao) TableViewCell
( TableViewCell.swift ). Los detalles de estas clases siguen a continuación.
Para este ejemplo simple, tenga en cuenta que no incorporaremos VC1 en un controlador de navegación (que de otra manera es apropiado para la vista de tabla -> ver navegación).
Comenzaremos en el guión gráfico, agregando objetos (arrastrando y soltando desde la biblioteca de objetos) para un Table View Controller
y un View Controller
. El contenedor de vista de tabla también contendrá automáticamente, en su Table View
, una TableViewCell
. Continuando en el guión gráfico:
- Agregue un objeto
TableViewCell
contenedorTableViewCell
enTable View Controller
(TableViewCell
como desee) - En
View Controller
, agregue un objeto deText Field
y un objeto deButton
(alinéelos como desee). - Establezca el punto de entrada al
Table View Controller
. - A continuación,
TableViewCell
presionada la tecla Ctrl y arrastre un segue ''Mostrar'' desde elTableViewCell
alView Controller
. - Seleccione
Show
segue y, desde el inspector de atributos, ingrese un identificador para él, por ejemplo, ShowDetail . - Finalmente, con
TableViewCell
seleccionado (como seTableViewCell
arriba, desde el inspector de atributos), ingrese un identificador para la celda. Aquí, usaremos simplemente use el identificador TableViewCell .
Ahora dejamos el guión gráfico por ahora e implementamos tres clases, asociadas con el Table View Controller
, el View Controller
y TableViewCell
los formadores.
Comenzamos con el Table View Controller
e implementamos nuestra UITableViewController
. Tenga en cuenta que aquí, en lugar de usar un NSMutableArray
para contener los textos del UITextLabel
en cada celda, simplemente usaremos un conjunto de UITextLabel
.
// TableViewController.swift
Import UIKit
class TableViewController: UITableViewController {
// Properties
var userTextLabels = [String]()
var numberOfCells: Int?
override func viewDidLoad() {
super.viewDidLoad()
numberOfCells = loadSampleTextLabels() // Load sample labels.
}
func loadSampleTextLabels() -> Int {
userTextLabels += ["Label #1", "Label #2", "Label #3"]
return userTextLabels.count
}
// func numberOfSectionsInTableView(tableView: UITableView) ...
// func tableView(tableView: UITableView, numberOfRowsInSection section: Int) ...
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = ("TableViewCell")
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! TableViewCell
// Text for current cell
let cellText = userTextLabels[indexPath.row]
cell.userSuppliedTextLabel.text = cellText
return cell
}
// ... communication?
}
Donde los dos métodos comentados son métodos estándar utilizados en cualquier UITableViewController
, para el número de secciones (por ejemplo, return 1
) y celdas (por ejemplo, return (numberOfCells ?? 0)
) en la tabla, respectivamente. Dejaré de arreglar esto para ti.
Ahora, asociamos los objetos TableViewCell
en la vista de tabla con instancias de una subclase a UITableViewCell
. Aquí, usaremos una clase muy simple para nuestras células; cada celda contiene una sola instancia de UILabel
(creada a través del guión gráfico Ctrl-arrastra como @IBOutlet
desde UILabel
en las celdas de la vista de tabla).
// TableViewCell.swift
import UIKit
class TableViewCell: UITableViewCell {
// Properties
@IBOutlet weak var userSuppliedTextLabel: UILabel!
// Ctrl-drag from UILabel (in TableViewCell) in storyboard
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
Finalmente, para el controlador de vista al que se accede desde las celdas de vista de tabla: use una sola @IBOutlet
para el UITextField
utilizado para la entrada de texto de usuario, y maneje los eventos en este campo de texto usando el UITextFieldDelegate
preexistente. P.ej:
// ViewController.swift
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
// Properties
@IBOutlet weak var userSuppliedText: UITextField!
// Ctrl-drag from storyboard...
var cellText: String?
override func viewDidLoad() {
super.viewDidLoad()
userSuppliedText.text = cellText ?? "..."
// Handle the user input in the text field through delegate callbacks
userSuppliedText.delegate = self
}
// UITextFieldDelegate
func textFieldShouldReturn(textField: UITextField) -> Bool {
// User finished typing (hit return): hide the keyboard.
textField.resignFirstResponder()
return true
}
func textFieldDidEndEditing(textField: UITextField) {
cellText = textField.text
}
}
También hemos declarado una propiedad de cadena ( cellText
) aquí, que actuará como contenedor para la comunicación entre VC1 y VC2.
Volvemos al guión gráfico y --- desde el inspector de identidad --- asociamos los tres objetos del guión gráfico ( Table View Controller
, View Controller
, TableViewCell
) con sus clases asociadas que acabamos de escribir.
Ahora estamos casi en nuestra meta; solo resta especificar cómo comunicarse entre los dos controladores.
Comenzaremos con la comunicación de VC1 a VC2. En su comentario anterior, estaba en el camino correcto (para esta solución específica, de todos modos) mirando el prepareForSegue(...)
. En la clase para Table View Controller
, agregamos el siguiente método:
// ... add to TableViewController.swift
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if segue.identifier == "ShowDetail" {
let viewController = segue.destinationViewController as! ViewController
if let selectedCell = sender as? TableViewCell {
let indexPath = tableView.indexPathForCell(selectedCell)!
let currentTextInCell = userTextLabels[indexPath.row]
viewController.cellText = currentTextInCell // <-- note this
}
}
}
Por lo tanto, para la comunicación VC1-> VC2, podemos (en este ejemplo) traer cualquier texto existente que esté ocupando el UILabel
en la celda del remitente (como lo especifica la matriz String userTextLabels
). Mire el viewDidLoad(...)
en ViewController.swift para ver cómo se pasa este valor desde VC1 y cómo se establece como texto predeterminado en UITextField
en VC2.
Ahora, para la comunicación VC2-> VC1, que era la dirección de comunicación específica sobre la que preguntaba, agregue otro método (programáticamente), nuevamente a TableViewController.swift :
// ... add to TableViewController.swift
@IBAction func unwindToTableView(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? ViewController,
text = sourceViewController.cellText {
// ^ note 2nd clause of if let statement above
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update cell text
userTextLabels[selectedIndexPath.row] = text
tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
}
}
}
Aquí, definimos una acción de desenrollado que, cuando se desencadena, recupera la propiedad cellText
del controlador de vista que fue el origen del segue, es decir, en nuestro caso, la instancia de ViewController
. Pero, ¿cómo desencadenamos esta acción?
Regrese al guión gráfico y al View Controller
. Tenga en cuenta los tres pequeños iconos en la parte superior del objeto View Controller
, más específicamente, el más derecho de ellos, llamado Exit
. Mantenga presionada la tecla Ctrl y arrastre una acción desde su Button
hasta el ícono de Exit
, y seleccione la unwindToTableView
acciones de unwindToTableView
. Cuando hace clic en el botón del controlador de vista, la vista se desenrolla (sale) y aterriza en el método unwindToTableView
en TableViewController
.
La aplicación resultante debería verse más o menos así:
Esto fue mucho más largo de lo que esperaba, pero una vez que empiezas a escribir ... De todos modos, el método anterior no usa, naturalmente, variables globales, pero hace uso de referencias a prepareForSegue
futuras ( prepareForSegue
) o históricas ( unwindToTableView
) para obtener ( generalmente desde la vista actual o histórica) o establecer (generalmente en el actual de la vista futura) usando estas referencias (para vista futura / histórica).
Apple tiene su propio tutorial muy completo sobre una aplicación de ejemplo en el contexto tableviewcontroller / viewcontroller que yo recomendaría revisar. Lo encontré muy valioso cuando comencé a programar Swift.
Tengo algunas dudas bastante simples con respecto a la unión opcional, variable global, envoltura y desenvolver. Como soy nuevo en SWIFT, es muy importante comprender las tetas y los bits de sus conceptos.
1) En Swift si declaro una variable global, tengo 2 opciones para hacerlo opcional o no opcional, así que déjame tener 2-4 o más variables opcionales. Entonces, ¿es recomendable vincular opcionalmente todas esas variables en
viewDidLoad() method// so that I could use them without any problem of unwrapping and fatal error in my program.
2) Permítanme dejarme más claro con el siguiente ejemplo: tengo 2 VC en mi proyecto VC1 y VC2. VC2 tiene un campo de texto en el que el usuario ingresa un valor y lo muestra en una tabla en VC1.
En Vc1
var namevc1 = NSMutableArray?//holds the input of textfield to be passed from VC2.
Como puede ver, mi VC1 es el primer controlador de vista que se carga cuando se ejecuta mi proyecto y estoy usando una variable opcional para llenar mi tabke vuew que es
''arr''
Entonces, cuando la aplicación se ejecuta por primera vez, está vacía. Por lo tanto, podría causar un error fatal al usar su valor en el código. Entonces, ¿cuál es su solución si desvincularlo en el
viewDidLoad()
método o en total declara un tipo de matriz NSMutable vacío en lugar de tipo opcional.
Gracias por adelantado.