Swift From Scratch Control de acceso y observadores de propiedades

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

Prerrequisitos

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.

1. Eliminar elementos

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

2. Marcar los artículos

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.

3. Estado de ahorro

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

Paso 1: Estado de carga

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.

  • Abrir / PUblic: Las entidades marcadas como abiertas o públicas son accesibles por las entidades definidas en el mismo módulo, así como otros módulos. Esto es ideal para exponer la interfaz de un marco. Hay varias diferencias entre los niveles de acceso abierto y público. Puedes leer más sobre estas diferencias en The Swift Programming Language.
  • Interno: Este es el nivel de acceso predeterminado. En otras palabras, si no se especifica ningún nivel de acceso, se aplica este nivel de acceso. Una entidad con un nivel de acceso interno solo es accesible por entidades definidas en el mismo módulo.
  • Archivo-Privado: Una entidad declarada como archivo privado solo es accesible por las entidades definidas en el mismo archivo fuente. Por ejemplo, los métodos de ayuda privada definidos en el ViewController clase solo son accesibles por la ViewController clase.
  • Privado: Private es muy similar a file-private. La única diferencia es que solo se puede acceder a una entidad declarada como privada desde la declaración adjunta. Por ejemplo, si creamos una extensión para el 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")

Paso 2: Estado de ahorro

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.

4. Observadores de la propiedad

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.

Paso 1: Agregar una etiqueta

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.

Paso 2: Implementando un observador de propiedades

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 cambiado
  • se estableció: invocado después de que el valor haya cambiado

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

Conclusión

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