Compartir vía


Edición de tablas con Xamarin.iOS

Las funciones de edición de tablas se habilitan anulando métodos en una subclase UITableViewSource. El comportamiento de edición más sencillo es el gesto de deslizar para borrar, que puede implementarse con un único método. La edición más compleja (incluido el desplazamiento de filas) puede realizarse con la tabla en modo de edición.

Deslizar el dedo para eliminar

La función de deslizar para borrar es un gesto natural de iOS que los usuarios esperan.

Ejemplo de deslizar el dedo a eliminar

Hay tres métodos que afectan al gesto de deslizar para mostrar un botón Eliminar en una celda:

  • CommitEditingStyle: el origen de la tabla detecta si este método está anulado y activa automáticamente el gesto de deslizar para borrar. La implementación del método debe llamar al DeleteRows en UITableView para hacer desaparecer las celdas, y también eliminar los datos subyacentes de su modelo (por ejemplo, una matriz, diccionario o base de datos).
  • CanEditRow: si se anula CommitEditingStyle, se asume que todas las filas son editables. Si este método se implementa y devuelve falso (para algunas filas específicas, o para todas las filas) entonces el gesto de deslizar para borrar no estará disponible en esa celda.
  • TitleForDeleteConfirmation: especifica opcionalmente el texto para el botón Eliminar. Si no se implementa este método, el texto del botón será "Eliminar".

Estos métodos se implementan en la clase TableSource:

public override void CommitEditingStyle (UITableView tableView, UITableViewCellEditingStyle editingStyle, Foundation.NSIndexPath indexPath)
{
    switch (editingStyle) {
        case UITableViewCellEditingStyle.Delete:
            // remove the item from the underlying data source
            tableItems.RemoveAt(indexPath.Row);
            // delete the row from the table
            tableView.DeleteRows (new NSIndexPath[] { indexPath }, UITableViewRowAnimation.Fade);
            break;
        case UITableViewCellEditingStyle.None:
            Console.WriteLine ("CommitEditingStyle:None called");
            break;
    }
}
public override bool CanEditRow (UITableView tableView, NSIndexPath indexPath)
{
    return true; // return false if you wish to disable editing for a specific indexPath or for all rows
}
public override string TitleForDeleteConfirmation (UITableView tableView, NSIndexPath indexPath)
{   // Optional - default text is 'Delete'
    return "Trash (" + tableItems[indexPath.Row].SubHeading + ")";
}

En este ejemplo UITableViewSource se ha actualizado para utilizar un List<TableItem> (en lugar de una matriz de cadenas) como fuente de datos, ya que admite la adición y eliminación de elementos de la colección.

Modo Editar

Cuando una tabla está en modo de edición, el usuario ve un widget rojo de "parar" en cada fila, que revela un botón Eliminar al tocarlo. La tabla también muestra un icono de "controlador" para indicar que se puede arrastrar la fila para cambiar el orden. El ejemplo TableEditMode implementa estas características como se muestra.

El ejemplo TableEditMode implementa estas características como se muestra

Existen varios métodos en UITableViewSource que afectan al comportamiento del modo de edición de una tabla:

  • CanEditRow: si se puede editar cada fila. Devuelve falso para evitar tanto el deslizamiento para borrar como el borrado mientras se está en modo edición.
  • CanMoveRow: devuelve verdadero para habilitar el "controlador" de movimiento o falso para impedir el movimiento.
  • EditingStyleForRow: cuando la tabla está en modo edición, el valor de retorno de este método determina si la celda muestra el icono rojo de borrar o el icono verde de agregar. Devuelve UITableViewCellEditingStyle.None si la fila no debe ser editable.
  • MoveRow: se llama cuando se mueve una fila para que la estructura de datos subyacente pueda modificarse para que coincida con los datos tal y como se muestran en la tabla.

La implementación de los tres primeros métodos es relativamente sencilla: a menos que desee utilizarlos indexPath para cambiar el comportamiento de filas específicas, simplemente codifique los valores de retorno para toda la tabla.

public override bool CanEditRow (UITableView tableView, NSIndexPath indexPath)
{
    return true; // return false if you wish to disable editing for a specific indexPath or for all rows
}
public override bool CanMoveRow (UITableView tableView, NSIndexPath indexPath)
{
    return true; // return false if you don't allow re-ordering
}
public override UITableViewCellEditingStyle EditingStyleForRow (UITableView tableView, NSIndexPath indexPath)
{
    return UITableViewCellEditingStyle.Delete; // this example doesn't support Insert
}

La implementación MoveRow es un poco más complicada porque necesita alterar la estructura de datos subyacente para que coincida con el nuevo orden. Debido a que los datos se implementan como List el código a continuación elimina el elemento de datos en su antigua ubicación y lo inserta en la nueva ubicación. Si los datos estuvieran almacenados en una tabla de base de datos SQLite con una columna "orden" (por ejemplo), este método tendría que realizar algunas operaciones SQL para reordenar los números en esa columna.

public override void MoveRow (UITableView tableView, NSIndexPath sourceIndexPath, NSIndexPath destinationIndexPath)
{
    var item = tableItems[sourceIndexPath.Row];
    var deleteAt = sourceIndexPath.Row;
    var insertAt = destinationIndexPath.Row;

    // are we inserting
    if (destinationIndexPath.Row < sourceIndexPath.Row) {
        // add one to where we delete, because we're increasing the index by inserting
        deleteAt += 1;
    } else {
        // add one to where we insert, because we haven't deleted the original yet
        insertAt += 1;
    }
    tableItems.Insert (insertAt, item);
    tableItems.RemoveAt (deleteAt);
}

Por último, para poner la tabla en modo de edición, el botón Editar debe llamar SetEditing así

table.SetEditing (true, true);

y cuando el usuario haya terminado de editar, el botón Hecho debería desactivar el modo de edición:

table.SetEditing (false, true);

Estilo de edición de inserción de filas

La inserción de filas desde dentro de la tabla es una interfaz de usuario poco común: el principal ejemplo en las aplicaciones estándar de iOS es la pantalla Editar contacto. Esta captura de pantalla muestra cómo funciona la función de inserción de filas: en el modo de edición hay una fila adicional que (al hacer clic) inserta filas adicionales en los datos. Una vez finalizada la edición, se elimina la fila temporal (agregar nueva).

Una vez completada la edición, se quita la fila de adición temporal nueva.

Existen varios métodos en UITableViewSource que afectan al comportamiento del modo de edición de una tabla. Estos métodos se han implementado de la siguiente manera en el código de ejemplo:

  • EditingStyleForRow: devuelve UITableViewCellEditingStyle.Delete para las filas que contienen datos, y devuelve UITableViewCellEditingStyle.Insert para la última fila (que se agregará específicamente para comportarse como un botón de inserción).
  • CustomizeMoveTarget: mientras el usuario mueve una celda, el valor de retorno de este método opcional puede anular su elección de ubicación. Esto significa que puede evitar que "suelten" la celda en ciertas posiciones, como este ejemplo que evita que cualquier fila se mueva después de la fila (agregar nueva).
  • CanMoveRow: devuelve verdadero para habilitar el "controlador" de movimiento o falso para impedir el movimiento. En el ejemplo, la última fila tiene el "controlador" de movimiento oculto porque está pensada para servir únicamente como botón de inserción.

También agregamos dos métodos personalizados para agregar la fila de "inserción" y volver a quitarla cuando ya no sea necesaria. Se les llama desde los botones de Editar y Listo :

  • WillBeginTableEditing: cuando se toca el botón Editar se llama SetEditing a poner la tabla en modo edición. Esto activa el método WillBeginTableEditing donde mostramos la fila (agregar nueva) al final de la tabla para actuar como un "botón de inserción".
  • DidFinishTableEditing: cuando se toca el botón Listo SetEditing se llama de nuevo para desactivar el modo de edición. El código de ejemplo elimina la fila (agregar nueva) de la tabla cuando la edición ya no es necesaria.

Estos métodos se implementan en el archivo de ejemplo TableEditModeAdd/Code/TableSource.cs:

public override UITableViewCellEditingStyle EditingStyleForRow (UITableView tableView, NSIndexPath indexPath)
{
    if (tableView.Editing) {
        if (indexPath.Row == tableView.NumberOfRowsInSection (0) - 1)
            return UITableViewCellEditingStyle.Insert;
        else
            return UITableViewCellEditingStyle.Delete;
    } else // not in editing mode, enable swipe-to-delete for all rows
        return UITableViewCellEditingStyle.Delete;
}
public override NSIndexPath CustomizeMoveTarget (UITableView tableView, NSIndexPath sourceIndexPath, NSIndexPath proposedIndexPath)
{
    var numRows = tableView.NumberOfRowsInSection (0) - 1; // less the (add new) one
    if (proposedIndexPath.Row >= numRows)
        return NSIndexPath.FromRowSection(numRows - 1, 0);
    else
        return proposedIndexPath;
}
public override bool CanMoveRow (UITableView tableView, NSIndexPath indexPath)
{
    return indexPath.Row < tableView.NumberOfRowsInSection (0) - 1;
}

Estos dos métodos personalizados se utilizan para añadir y eliminar la fila (agregar nueva) cuando el modo de edición de la tabla está habilitado o deshabilitado:

public void WillBeginTableEditing (UITableView tableView)
{
    tableView.BeginUpdates ();
    // insert the 'ADD NEW' row at the end of table display
    tableView.InsertRows (new NSIndexPath[] {
            NSIndexPath.FromRowSection (tableView.NumberOfRowsInSection (0), 0)
        }, UITableViewRowAnimation.Fade);
    // create a new item and add it to our underlying data (it is not intended to be permanent)
    tableItems.Add (new TableItem ("(add new)"));
    tableView.EndUpdates (); // applies the changes
}
public void DidFinishTableEditing (UITableView tableView)
{
    tableView.BeginUpdates ();
    // remove our 'ADD NEW' row from the underlying data
    tableItems.RemoveAt ((int)tableView.NumberOfRowsInSection (0) - 1); // zero based :)
    // remove the row from the table display
    tableView.DeleteRows (new NSIndexPath[] { NSIndexPath.FromRowSection (tableView.NumberOfRowsInSection (0) - 1, 0) }, UITableViewRowAnimation.Fade);
    tableView.EndUpdates (); // applies the changes
}

Finalmente, este código instancia los botones Editar y Listo, con lambdas que habilitan o deshabilitan el modo edición cuando se tocan:

done = new UIBarButtonItem(UIBarButtonSystemItem.Done, (s,e)=>{
    table.SetEditing (false, true);
    NavigationItem.RightBarButtonItem = edit;
    tableSource.DidFinishTableEditing(table);
});
edit = new UIBarButtonItem(UIBarButtonSystemItem.Edit, (s,e)=>{
    if (table.Editing)
        table.SetEditing (false, true); // if we've half-swiped a row
    tableSource.WillBeginTableEditing(table);
    table.SetEditing (true, true);
    NavigationItem.LeftBarButtonItem = null;
    NavigationItem.RightBarButtonItem = done;
});

Este patrón de interfaz de usuario de inserción de filas no se utiliza muy a menudo, sin embargo, también puede utilizar los métodos UITableView.BeginUpdates y EndUpdates para animar la inserción o eliminación de celdas en cualquier tabla. La regla para utilizar estos métodos es que la diferencia en el valor devuelto por RowsInSection entre las llamadas BeginUpdates y EndUpdates debe coincidir con el número neto de celdas añadidas o eliminadas con los métodos InsertRows y DeleteRows. Si no se cambia la fuente de datos subyacente para que coincida con las inserciones o eliminaciones en la vista de tabla, se producirá un error.