DEV Community

GoyesDev
GoyesDev

Posted on

[SUI] Editando un List

Los siguientes modificadores sirven para manipular los datos de un List:

Los modificadores onMove(perform:) y onDelete(perform:) producen un valor de tipo IndexSet que contiene los índices de los valores a cambiar de un arreglo con los métodos:

Cambios visuales automáticos

Al aplicar el modificador - onDelete(perform:) sobre List, el sistema automáticamente habilita la funcionalidad que permite arrastrar las filas hacia la izquierda para hacer visible el botón de "Borrar". Si la lista se crea con un ForEach, entonces se lo debe envolver con List.

struct ContentView: View {
  @State var contacts: [Contact] = [
      .init(name: "Luis"),
      .init(name: "David"),
      .init(name: "Goyes"),
      .init(name: "Garcés"),
  ]

  var body: some View {
    List {
      ForEach(contacts) { contact in
        Text(contact.name)
      }
      .onDelete { indexSet in
        contacts.remove(atOffsets: indexSet)
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Modo de edición

Para seleccionar, eliminar y mover filas, la lista debe estar en modo de edición. El modo de edición es un valor del ambiente llamado editMode que se manipula con un Binding.

La forma más fácil de activar el modo de edición es con la vista EditButton que conmuta el valor de editMode.

struct ContentView: View {
  @State var contacts: [Contact] = [
      .init(name: "Luis"),
      .init(name: "David"),
      .init(name: "Goyes"),
      .init(name: "Garcés"),
  ]

  var body: some View {
    NavigationStack {
      List {
        ForEach(contacts) { contact in
          Text(contact.name)
        }
        .onDelete { indexSet in
          contacts.remove(atOffsets: indexSet)
        }
        .onMove { indexSet, to in
          contacts.move(fromOffsets: indexSet, toOffset: to)
        }
      }
      .toolbar {
        EditButton()
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Selección de filas

Para seleccionar una o más filas, debemos inicialiar el List con un Binding en el argumento selection para almacenar el identificador de una o más filas.

Para seleccionar una sola fila, el modo de edición está habilitado por defecto.

struct ContentView: View {
  @State var contacts: [Contact] = [
      .init(name: "Luis"),
      .init(name: "David"),
      .init(name: "Goyes"),
      .init(name: "Garcés"),
  ]

  @State var selectedContact: Contact.ID?

  var body: some View {
    NavigationStack {
      List(selection: $selectedContact) {
        ForEach(contacts) { contact in
          Text(contact.name)
        }
      }
      .toolbar {
        Button {
          if let index = contacts.firstIndex(where: { $0.id == selectedContact }) {
            contacts.remove(at: index)
            selectedContact = nil
          }
        } label: {
          Label {
            Text("Eliminar")
          } icon: {
            Image(systemName: "trash")
          }
        }.disabled(selectedContact == nil ? true : false)
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Para seleccionar múltiples filas, es necesario activar manualmente el modo de edición. Cuando se activa el modo de edición, aparece un "checkbox" al lado izquierdo de la fila para seleccionarla. En el siguiente ejemplo se usa EditButton:

struct ContentView: View {
  @State var contacts: [Contact] = [
      .init(name: "Luis"),
      .init(name: "David"),
      .init(name: "Goyes"),
      .init(name: "Garcés"),
  ]

  @State var selectedContacts: Set<Contact.ID> = []

  var body: some View {
    NavigationStack {
      List(selection: $selectedContacts) {
        ForEach(contacts) { contact in
          Text(contact.name)
        }
      }
      .toolbar {
        EditButton()

        Button {
          let indexes = selectedContacts.compactMap { id in
            contacts.firstIndex { $0.id == id }
          }.reduce(into: IndexSet()) { partialResult, index in
            partialResult.insert(index)
          }

          contacts.remove(atOffsets: indexes)
          selectedContacts = []
        } label: {
          Label {
            Text("Eliminar")
          } icon: {
            Image(systemName: "trash")
          }
        }.disabled(selectedContacts.count == 0)
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Manipulando editMode en el ambiente

Para deshabilitar el modo de edición del List de forma programática, se debe inyectar un valor de EditMode por medio del modificador environment(_:_:), con la llave editMode.

Ya no se va a usar EditButton, sino que se va a crear un botón que cambie el estado de edición de la lista por medio de un binding de tipo EditMode. Aunque se podría usar un booleano, e inyectar el EditMode por medio de un Binding de tipo .constant, de esta manera se perdería el caracter reactivo de la inyección del binding al ambiente.

struct ContentView: View {
  @State var contacts: [Contact] = [
      .init(name: "Luis"),
      .init(name: "David"),
      .init(name: "Goyes"),
      .init(name: "Garcés"),
  ]

  @State var selectedContacts: Set<Contact.ID> = []
  @State var editing: EditMode = .inactive

  var body: some View {
    NavigationStack {
      List(selection: $selectedContacts) {
        ForEach(contacts) { contact in
          Text(contact.name)
        }
      }
      .environment(\.editMode, $editing)
      .deleteDisabled(false)
      .toolbar {
        Button {
          editing = (editing == .inactive) ? .active : .inactive
        } label: {
          Text(editing == .active ? "Listo" : "Editar")
        }

        Button {
          let indexes = selectedContacts.compactMap { id in
            contacts.firstIndex { $0.id == id }
          }.reduce(into: IndexSet()) { partialResult, index in
            partialResult.insert(index)
          }

          contacts.remove(atOffsets: indexes)
          selectedContacts = []
          editing = .inactive
        } label: {
          Label {
            Text("Eliminar")
          } icon: {
            Image(systemName: "trash")
          }
        }.disabled(selectedContacts.count == 0)
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)