Contenido del curso

Operaciones CRUD en un proyecto con MVVM

Marcar tareas completadas en SwiftUI

Resumen

Marcar una tarea como completada es la acción que cierra el ciclo de cualquier app de pendientes. Aquí aprendes a construir un TodoStatusView en SwiftUI que alterna entre un círculo vacío y un checkmark, conectarlo al ViewModel y reutilizarlo en el listado, el detalle y los archivados. Va dirigido a quien ya tiene una app de to dos funcional y solo le falta esta última pieza.

Cómo crear el TodoStatusView desde cero en SwiftUI

La idea es simple: una vista pequeña en la esquina superior derecha del ítem que muestre si la tarea está hecha o no. Tú creas el archivo TodoStatusView manualmente y armas su estructura básica para luego conectarla al modelo.

Dentro de la vista necesitas tres piezas clave que se conectan con el resto de la app [05:30]:

  • Una referencia al ViewModel con @EnvironmentObject para acceder a las funciones globales.
  • Una variable pública todo de tipo TodoEntity que recibe la tarea desde afuera.
  • Una variable disabled de tipo Bool que controla si el usuario puede o no tocar el ícono.

En el init inicializas el todo con un TodoEntity y el parámetro disabled arranca en false. Esto te deja preparada la vista para recibir cualquier tarea del listado.

¿Qué hace EnvironmentObject en SwiftUI? Te permite compartir un objeto observable, como tu ViewModel, entre varias vistas sin pasarlo manualmente por cada inicializador.

Cómo alternar el estado isComplete con la función toggle

En el ViewModel tienes la función updateTodoStatus, y aquí ocurre la magia. Primero obtienes el índice del to do con getTodoIndex. Si no lo encuentra, sales con un return. Si lo encuentra, accedes a la colección por ese índice y modificas la propiedad isComplete.

En lugar de escribir un if/else para invertir el booleano, usas la función nativa toggle() [02:45]. Si está en true, lo pasa a false; si está en false, lo pasa a true. Después llamas a la función que guarda los datos y listo.

¿Para qué sirve toggle en una variable booleana? Invierte automáticamente su valor sin necesidad de comparar. Reduce el código a una sola línea y evita errores de lógica.

Cómo elegir el ícono según isComplete

Dentro del body de TodoStatusView defines una variable imageName de tipo String usando un operador ternario sobre todo.isComplete:

  • Si la tarea está completa, usas checkmark.circle.fill.
  • Si no lo está, usas circle, que es un círculo vacío del sistema.

Ambos íconos vienen de SF Symbols, así que no necesitas importar recursos externos. Después agregas variables width y height de tipo CGFloat para controlar el tamaño, normalmente 24 unidades para el círculo vacío y un poco menos para el checkmark lleno.

La imagen final la retornas con Image(systemName: imageName), le aplicas .resizable() y un .frame(width: width, height: height) para que respete el tamaño definido.

Dónde mostrar el TodoStatusView en la app

Esta vista no vive sola. La idea es reutilizarla en tres lugares distintos según el contexto.

Cómo integrarlo en TodoItemView y el preview

En TodoItemView, justo después del Spacer (alrededor de la línea 28 o 29), agregas TodoStatusView(todo: todo). El Spacer empuja el ícono a la esquina derecha, que es exactamente donde lo quieres visualmente [10:15].

En TodoPreviewView, que es la pantalla de detalle, lo colocas después del bloque de fecha y título, antes del detalle. Si aparece centrado, mueves la llamada después del Spacer para que se alinee a la derecha.

Cómo agregar el evento de toque con onTapGesture

Para que el ícono responda al tap, dentro de TodoStatusView agregas el modificador .onTapGesture y llamas a viewModel.updateTodoStatus(todo). Justo después encadenas .disabled(disabled) para que, cuando esa propiedad sea true, el gesto se ignore.

Esto te da control total: en el listado activo el usuario puede marcar y desmarcar, pero en otras vistas puedes bloquearlo pasando disabled: true.

Cómo mostrar el estado en tareas archivadas sin permitir edición

Las tareas archivadas también deberían mostrar si están completadas, pero sin que el usuario pueda cambiarlas. Aquí entra TodoArchiveItemView.

Reestructuras la vista creando un VStack con alineación .leading y, dentro, un HStack con alineación .center. El orden visual queda así:

  1. Primero el TodoStatusView(todo: todo) con el modificador .disabled(true).
  2. Después el título con font: .headline y lineLimit(1).
  3. Debajo, la fecha usando Text(todo.date, format: .dateTime) mostrando día, mes y año.

El resultado es una fila clara: ves el estado, el título y la fecha en una sola mirada, pero el ícono no responde al tap porque disabled está en true. Esa es la utilidad real de haber expuesto esa propiedad desde el inicio.

Con esto, la variable isComplete viaja por toda la app y su único trabajo es alternar entre true y false. ¿Tú dónde más lo aplicarías en tu propia app de tareas? Cuéntame en los comentarios.