En la lección anterior, agregamos la capacidad de crear elementos de tareas pendientes. Si bien esta adición ha hecho que la aplicación sea un poco más útil, también sería conveniente agregar la capacidad de marcar elementos como hechos y eliminar elementos. En eso nos centraremos en esta lección..
Si desea seguirme conmigo, asegúrese de tener Xcode 8.3.2 o superior instalado en su máquina. Puedes descargar Xcode 8.3.2 desde la App Store de Apple.
Para eliminar elementos, necesitamos implementar dos métodos adicionales de UITableViewDataSource
protocolo. Primero debemos indicar a la vista de tabla qué filas se pueden editar implementando el tableView (_: canEditRowAt :)
método. Como puede ver en el siguiente fragmento de código, la implementación es sencilla. Le decimos a la vista de tabla que cada fila se puede editar al regresar cierto
.
func tableView (_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool return true
El segundo método que nos interesa es tableView (_: commit: forRowAt :)
. La implementación es un poco más compleja pero lo suficientemente fácil de entender.
func tableView (_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) if editingStyle == .delete // Elementos de actualización de elementos. : [indexPath], con: .right)
Comenzamos por comprobar el valor de ediciónEstilo
, una enumeración de tipo UITableViewCellEditingStyle
. Solo eliminamos un elemento si el valor de ediciónEstilo
es igual a UITableViewCellEditingStyle.delete
.
Sin embargo, Swift es más inteligente que eso. Porque sabe que ediciónEstilo
es de tipo UITableViewCellEditingStyle
, podemos omitir UITableViewCellEditingStyle
, El nombre de la enumeración, y escribir. .borrar
, el valor de miembro de la enumeración que nos interesa. Si eres nuevo en las enumeraciones en Swift, entonces te recomiendo que leas este breve consejo sobre las enumeraciones en Swift..
A continuación, actualizamos la fuente de datos de la vista de tabla., artículos
, invocando quitar (en :)
sobre el artículos
Propiedad, pasando en el índice correcto. También actualizamos la vista de tabla invocando. deleteRows (en: con :)
en tableView
, pasando en una matriz con indexPath
y .Correcto
para especificar el tipo de animación. Como vimos anteriormente, podemos omitir el nombre de la enumeración, UITableViewRowAnimation
, ya que Swift sabe que el tipo del segundo argumento es UITableViewRowAnimation
.
El usuario ahora debería poder eliminar elementos de la lista. Construye y ejecuta la aplicación para probar esto..
Para marcar un elemento como hecho, vamos a agregar una marca de verificación a la fila correspondiente. Esto implica que debemos realizar un seguimiento de los elementos que el usuario ha marcado como hechos. Para ese propósito, declararemos una nueva propiedad que administre esto por nosotros. Declarar una propiedad variable, artículos revisados
, de tipo [Cuerda]
, e inicializarlo con una matriz vacía.
var checkedItems: [String] = []
En tableView (_: cellForRowAt :)
, comprobamos si artículos revisados
contiene el artículo respectivo invocando el contiene (_ :)
Método, pasando en el elemento que corresponde con la fila actual. El método devuelve cierto
Si artículos revisados
contiene ít
.
func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell // Fetch Item let item = items [indexPath.row] // Dequeue Cell let cell = tableView.dequeueReusableCell (withIdentifier: "TableViewCell", para: indexPath ) // Configurar la celda cell.textLabel? .Text = item si checkedItems.contains (item) cell.accessoryType = .checkmark else cell.accessoryType = .none return cell
Si ít
se encuentra en artículos revisados
, configuramos el celular tipo de accesorio
propiedad a .marca de verificación
, un valor miembro de la UITableViewCellAccessoryType
enumeración. Si ít
no se encuentra, volvemos a .ninguna
como el tipo de accesorio de la célula.
El siguiente paso es agregar la capacidad de marcar un elemento como hecho al implementar un método de UITableViewDelegate
protocolo, tableView (_: didSelectRowAt :)
. En este método de delegado, primero llamamos deselectRow (at: animated :)
en tableView
para deseleccionar la fila que el usuario pulsó.
// MARK: - Table View Delegate Methods func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) tableView.deselectRow (at: indexPath, animated: true) // Fetch Item let item = elementos [indexPath.row] // Fetch Cell let cell = tableView.cellForRow (at: indexPath) // Find Index of Item let index = checkedItems.index (of: item) if let index = index checkedItems.remove (at: index) cell? .AccessoryType =. ninguna else checkedItems.append (item) cell? .accessoryType = .checkmark
A continuación, obtenemos el elemento correspondiente de artículos
y una referencia a la celda que corresponde con la fila marcada. Le pedimos artículos revisados
para el índice del ítem correspondiente invocando índice de:)
. Este método devuelve un opcional En t
. Si artículos revisados
contiene ít
, lo eliminamos de artículos revisados
y establecer el tipo de accesorio de la celda para .ninguna
. Si artículos revisados
no contiene ít
, lo agregamos a artículos revisados
y establecer el tipo de accesorio de la celda para .marca de verificación
.
Con estas adiciones, el usuario ahora puede marcar los elementos como hechos. Cree y ejecute la aplicación para asegurarse de que todo funciona como se espera.
La aplicación actualmente no guarda el estado entre los lanzamientos. Para resolver esto, vamos a almacenar el artículos
y artículos revisados
Arreglos en la base de datos predeterminada de usuario de la aplicación..
Comienza creando dos métodos de ayuda., loadItems ()
y loadCheckedItems ()
. Nota la privado
palabra clave prefijando cada método auxiliar. los privado
La palabra clave le dice a Swift que estos métodos solo son accesibles desde el interior del ViewController
clase.
// MARK: Private Helper Methods private func loadItems () let userDefaults = UserDefaults.standard si let items = userDefaults.object (forKey: "items") como? [String] self.items = items función privada loadCheckedItems () let userDefaults = UserDefaults.standard si let checkedItems = userDefaults.object (forKey: "checkedItems") como? [String] self.checkedItems = checkedItems
los privado
palabra clave es parte de Swift control de acceso. Como su nombre lo indica, el control de acceso define qué código tiene acceso a qué código. Los niveles de acceso se aplican a métodos, funciones, tipos, etc. Apple simplemente se refiere a entidades. Hay cinco niveles de acceso: abierto, público, interno, privado de archivos y privado.
ViewController
clase solo son accesibles por la ViewController
clase.ViewController
clase en ViewController.swift, cualquier entidad que esté marcada como archivo-privado no sería accesible en la extensión, pero las entidades privadas serían accesibles.La implementación de los métodos de ayuda es simple si está familiarizado con el UserDefaults
clase. Para facilitar el uso, almacenamos una referencia al objeto predeterminado de usuario estándar en una constante llamada UserDefaults
. En el caso de loadItems ()
, le pedimos UserDefaults
para el objeto asociado a la clave "artículos"
y abatirlo a una matriz opcional de cadenas. Desenvolvemos con seguridad lo opcional, lo que significa que almacenamos el valor en la constante artículos
si el opcional no es nulo
, y asignar el valor a la artículos
propiedad del controlador de vista.
Si el Si
La declaración parece confusa, luego eche un vistazo a una versión más sencilla de loadItems ()
Método en el siguiente ejemplo. El resultado es idéntico; la única diferencia es la concisión.
private func loadItems () dejar que userDefaults = UserDefaults.standard let StoresItems = userDefaults.object (forKey: "items") como? [Cadena] si deja elementos = StoresItems self.items = items
La implementación de loadCheckedItems ()
es idéntico, excepto por la clave utilizada para cargar el objeto almacenado en la base de datos predeterminada del usuario. Pongamos loadItems ()
y loadCheckedItems ()
para usar actualizando el viewDidLoad ()
método.
anular func viewDidLoad () super.viewDidLoad () // Establecer título title = "To Do" // Rellenar elementos de elementos = ["Buy Milk", "Finish Tutorial", "Play Minecraft"] // Load State loadItems () loadCheckedItems () // Clase de registro para la reutilización de células tableView.register (UITableViewCell.self, forCellReuseIdentifier: "TableViewCell")
Para guardar el estado, implementamos dos métodos más de ayuda privada., saveItems ()
y saveCheckedItems ()
. La lógica es similar a la de loadItems ()
y loadCheckedItems ()
. La diferencia es que almacenamos datos en la base de datos predeterminada de usuario. Asegúrese de que las teclas utilizadas en el setObject (_: forKey :)
las llamadas coinciden con las utilizadas en loadItems ()
y loadCheckedItems ()
.
private func saveItems () let userDefaults = UserDefaults.standard // Actualizar User Defaults userDefaults.set (items, forKey: "items") userDefaults.synchronize () private func saveCheckedItems () dejar userDefaults.standard //standard Valores predeterminados del usuario userDefaults.set (checkedItems, forKey: "checkedItems") userDefaults.synchronize ()
los sincronizar()
La llamada no es estrictamente necesaria. El sistema operativo se asegurará de que los datos que almacena en la base de datos predeterminada del usuario se escriban en el disco en algún momento. Invocando sincronizar()
, sin embargo, explícitamente le dice al sistema operativo que escriba cualquier cambio pendiente en el disco. Esto es útil durante el desarrollo, porque el sistema operativo no escribirá sus cambios en el disco si mata la aplicación. Entonces puede parecer que algo no está funcionando correctamente..
Necesitamos invocar saveItems ()
y saveCheckedItems ()
en varios lugares Para empezar llama saveItems ()
cuando se agrega un nuevo elemento a la lista. Hacemos esto en el método delegado de AddItemViewControllerDelegate
protocolo.
// MARCA: Agregar elemento Ver Controlador Delegar Métodos func controller (_ controlador: AddItemViewController, didAddItem: String) // Actualizar la fuente de datos items.append (didAddItem) // Guardar estado saveItems () // Volver a cargar la tabla Vista de tabla tableView.reloadData ( ) // Descartar Agregar elemento Ver Controlador despedir (animado: verdadero)
Cuando el estado de un elemento cambia en el tableView (_: didSelectRowAt :)
, actualizamos artículos revisados
. Es una buena idea invocar también. saveCheckedItems ()
en ese punto.
// MARK: - Table View Delegate Methods func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) tableView.deselectRow (at: indexPath, animated: true) // Fetch Item let item = elementos [indexPath.row] // Fetch Cell let cell = tableView.cellForRow (at: indexPath) // Find Index of Item let index = checkedItems.index (of: item) if let index = index checkedItems.remove (at: index) cell? .AccessoryType =. ninguna else checkedItems.append (item) cell? .accessoryType = .checkmark // Guardar estado saveCheckedItems ()
Cuando se elimina un elemento, ambos artículos
y artículos revisados
se actualizan Para guardar este cambio, llamamos a los dos. saveItems ()
y saveCheckedItems ()
.
func tableView (_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) if editingStyle == .delete // Fetch Item let item = items [indexPath.row] // Actualizar elementos items.remove (at: indexPath .row) if let index = checkedItems.index (of: item) checkedItems.remove (at: index) // Update Table View tableView.deleteRows (at: [indexPath], con: .right) // Save State saveItems () saveCheckedItems ()
Eso es. Construye y ejecuta la aplicación para probar tu trabajo. Juega con la aplicación y fuerza a salir. Cuando vuelva a iniciar la aplicación, el último estado conocido debe estar cargado y visible.
La experiencia de usuario de la aplicación es un poco escasa en este momento. Cuando se eliminan todos los elementos o cuando se lanza la aplicación por primera vez, el usuario ve una vista de tabla vacía. Esto no es genial Podemos resolver esto mostrando un mensaje cuando no hay elementos. Esto también me dará la oportunidad de mostrarte otra característica de Swift, observadores de propiedad.
Comencemos agregando una etiqueta a la interfaz de usuario para mostrar el mensaje. Declara una salida llamada mensajeLabel
de tipo UILabel
en el ViewController
clase abierta Main.storyboard, y agregar una etiqueta a la vista del controlador de vista.
@IBOutlet var messageLabel: UILabel!
Agregue las restricciones de diseño necesarias a la etiqueta y conéctela con el controlador de vista mensajeLabel
salida en el Inspector de conexiones. Establezca el texto de la etiqueta en Usted no tiene ninguna tarea pendiente. y centrar el texto de la etiqueta en el Inspector de atributos.
La etiqueta del mensaje solo debería ser visible si artículos
No contiene elementos. Cuando eso sucede, también debemos ocultar la vista de tabla. Podríamos resolver este problema agregando varios controles en el ViewController
clase, pero un enfoque más conveniente y elegante es utilizar un observador de propiedad.
Como su nombre lo indica, los observadores de la propiedad observan una propiedad. Se invoca un observador de propiedades cuando cambia una propiedad, incluso cuando el nuevo valor es el mismo que el valor anterior. Hay dos tipos de observadores de la propiedad..
establecerá
: invocado antes de que el valor haya cambiadose estableció
: invocado después de que el valor haya cambiadoPara nuestro propósito, implementaremos el se estableció
observador de la artículos
propiedad. Eche un vistazo a la sintaxis en el siguiente fragmento de código.
var items: [String] = [] didSet let hasItems = items.count> 0 tableView.isHidden =! hasItems messageLabel.isHidden = hasItems
La construcción puede parecer un poco extraña al principio, así que déjame explicarte lo que está sucediendo. Cuando el se estableció
Se invoca el observador de la propiedad, después de la artículos
La propiedad ha cambiado, verificamos si el artículos
La propiedad contiene cualquier elemento. Basado en el valor de la hasItems
Constante, actualizamos la interfaz de usuario. Es tan simple como eso.
los se estableció
Al observador se le pasa un parámetro constante que contiene el valor del valor antiguo de la propiedad. Se omite en el ejemplo anterior, porque no lo necesitamos en nuestra implementación. El siguiente ejemplo muestra cómo podría usarse.
var items: [String] = [] didSet (oldValue) if oldValue! = items let hasItems = items.count> 0 tableView.isHidden =! hasItems messageLabel.isHidden = hasItems
los valor antiguo
El parámetro en el ejemplo no tiene un tipo explícito, porque Swift conoce el tipo de artículos
propiedad. En el ejemplo, solo actualizamos la interfaz de usuario si el valor antiguo difiere del valor nuevo.
UNA establecerá
El observador trabaja de manera similar. La principal diferencia es que el parámetro pasado a la establecerá
El observador es una constante que mantiene el nuevo valor de la propiedad. Cuando utilice observadores de propiedades, tenga en cuenta que no se invocan cuando se inicializa la instancia.
Crea y ejecuta la aplicación para asegurarte de que todo está conectado correctamente. A pesar de que la aplicación no es perfecta y podría usar algunas funciones más, usted creó su primera aplicación iOS usando Swift.
En el transcurso de las últimas tres lecciones de esta serie, creó una aplicación funcional de iOS utilizando las características orientadas a objetos de Swift. Si tiene experiencia en programación y desarrollo de aplicaciones, entonces debe haber notado que el modelo de datos actual tiene algunos inconvenientes, para ponerlo a la ligera..
Almacenar elementos como cadenas y crear una matriz separada para almacenar el estado de un elemento no es una buena idea si está creando una aplicación adecuada. Un mejor enfoque sería crear una Que hacer
Clase para modelar elementos y almacenarlos en la caja de arena de la aplicación. Ese será nuestro objetivo para la próxima lección de esta serie..
Mientras tanto, echa un vistazo a algunos de nuestros otros cursos y tutoriales sobre el desarrollo de Swift en iOS.!