Share via


Preenchendo uma tabela com dados no Xamarin.iOS

Para adicionar linhas a um UITableView você precisa implementar uma UITableViewSource subclasse e substituir os métodos que o modo de exibição de tabela chama para preencher a si mesmo.

Este guia abrange:

  • Subclassificando um UITableViewSource
  • Reutilização de células
  • Adicionando um índice
  • Adicionando cabeçalhos e rodapés

Subclassificando UITableViewSource

Uma UITableViewSource subclasse é atribuída a cada UITableViewarquivo . O modo de exibição de tabela consulta a classe de origem para determinar como renderizar a si mesmo (por exemplo, quantas linhas são necessárias e a altura de cada linha, se diferente do padrão). Mais importante, a origem fornece cada exibição de célula preenchida com dados.

Há apenas dois métodos obrigatórios necessários para fazer uma tabela exibir dados:

  • RowsInSection – retorna uma nint contagem do número total de linhas de dados que a tabela deve exibir.
  • GetCell – retorna um UITableViewCell preenchido com dados para o índice de linha correspondente passado para o método.

O arquivo de exemplo BasicTable TableSource.cs tem a implementação mais simples possível do UITableViewSource. Você pode ver no trecho de código abaixo que ele aceita uma matriz de cadeias de caracteres para exibir na tabela e retorna um estilo de célula padrão contendo cada cadeia de caracteres:

public class TableSource : UITableViewSource {

        string[] TableItems;
        string CellIdentifier = "TableCell";

        public TableSource (string[] items)
        {
            TableItems = items;
        }

        public override nint RowsInSection (UITableView tableview, nint section)
        {
            return TableItems.Length;
        }

        public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
        {
            UITableViewCell cell = tableView.DequeueReusableCell (CellIdentifier);
            string item = TableItems[indexPath.Row];

            //if there are no cells to reuse, create a new one
            if (cell == null)
            {
                cell = new UITableViewCell (UITableViewCellStyle.Default, CellIdentifier);
            }

            cell.TextLabel.Text = item;

            return cell;
        }
}

Um UITableViewSource pode usar qualquer estrutura de dados, desde uma matriz de cadeia de caracteres simples (como mostrado neste exemplo) até uma Lista <> ou outra coleção. A implementação de UITableViewSource métodos isola a tabela da estrutura de dados subjacente.

Para usar essa subclasse, crie uma matriz de cadeia de caracteres para construir a origem e, em seguida, atribua-a a uma instância de UITableView:

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();
    table = new UITableView(View.Bounds); // defaults to Plain style
    string[] tableItems = new string[] {"Vegetables","Fruits","Flower Buds","Legumes","Bulbs","Tubers"};
    table.Source = new TableSource(tableItems);
    Add (table);
}

A tabela resultante tem esta aparência:

Tabela de exemplo em execução

A maioria das tabelas permite que o usuário toque em uma linha para selecioná-la e executar alguma outra ação (como reproduzir uma música, chamar um contato ou mostrar outra tela). Para conseguir isso, há algumas coisas que precisamos fazer. Primeiro, vamos criar um AlertController para exibir uma mensagem quando o usuário clicar em uma linha, adicionando o seguinte ao RowSelected método:

public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
    UIAlertController okAlertController = UIAlertController.Create ("Row Selected", tableItems[indexPath.Row], UIAlertControllerStyle.Alert);
    okAlertController.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, null));
    ...

    tableView.DeselectRow (indexPath, true);
}

Em seguida, crie uma instância do nosso View Controller:

HomeScreen owner;

Adicione um construtor à sua classe UITableViewSource que usa um controlador de exibição como um parâmetro e o salva em um campo:

public TableSource (string[] items, HomeScreen owner)
{
    ...
    this.owner = owner;

}

Modifique o método ViewDidLoad onde a classe UITableViewSource é criada para passar a this referência:

table.Source = new TableSource(tableItems, this);

Finalmente, de volta ao seu RowSelected método, chame PresentViewController o campo armazenado em cache:

public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
    ...
    owner.PresentViewController (okAlertController, true, null);

    ...
}

Agora o usuário pode tocar em uma linha e um alerta aparecerá:

O alerta de linha selecionada

Reutilização de células

Neste exemplo, há apenas seis itens, portanto, não é necessária a reutilização de células. Ao exibir centenas ou milhares de linhas, no entanto, seria um desperdício de memória criar centenas ou milhares de UITableViewCell objetos quando apenas alguns cabem na tela de cada vez.

Para evitar essa situação, quando uma célula desaparece da tela, sua exibição é colocada em uma fila para reutilização. À medida que o usuário rola, a tabela chama GetCell para solicitar que novas exibições sejam exibidas – para reutilizar uma célula existente (que não está sendo exibida no momento), basta chamar o DequeueReusableCell método. Se uma célula estiver disponível para reutilização, ela será retornada, caso contrário, um null será retornado e seu código deverá criar uma nova instância de célula.

Este trecho de código do exemplo demonstra o padrão:

// request a recycled cell to save memory
UITableViewCell cell = tableView.DequeueReusableCell (cellIdentifier);
// if there are no cells to reuse, create a new one
if (cell == null)
    cell = new UITableViewCell (UITableViewCellStyle.Default, cellIdentifier);

O cellIdentifier efetivamente cria filas separadas para diferentes tipos de célula. Neste exemplo, todas as células têm a mesma aparência, portanto, apenas um identificador codificado é usado. Se houver diferentes tipos de célula, cada um deles deve ter uma cadeia de caracteres de identificador diferente, tanto quando eles são instanciados quanto quando eles são solicitados da fila de reutilização.

Reutilização de células no iOS 6+

O iOS 6 adicionou um padrão de reutilização de células semelhante ao da introdução com as Visualizações de Coleção. Embora o padrão de reutilização existente mostrado acima ainda seja suportado para compatibilidade com versões anteriores, esse novo padrão é preferível, pois elimina a necessidade da verificação nula na célula.

Com o novo padrão, um aplicativo registra a classe de célula ou xib a ser usada chamando um RegisterClassForCellReuse ou RegisterNibForCellReuse no construtor do controlador. Em seguida, ao desenfileirar a célula no GetCell método, basta chamar DequeueReusableCell passando o identificador que você registrou para a classe de célula ou xib e o caminho do índice.

Por exemplo, o código a seguir registra uma classe de célula personalizada em um UITableViewController:

public class MyTableViewController : UITableViewController
{
  static NSString MyCellId = new NSString ("MyCellId");

  public MyTableViewController ()
  {
    TableView.RegisterClassForCellReuse (typeof(MyCell), MyCellId);
  }
  ...
}

Com a classe MyCell registrada, a célula pode ser desenfileirada GetCell no método do sem a necessidade da UITableViewSource verificação nula extra, como mostrado abaixo:

class MyTableSource : UITableViewSource
{
  public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
  {
    // if cell is not available in reuse pool, iOS will create one automatically
    // no need to do null check and create cell manually
    var cell = (MyCell) tableView.DequeueReusableCell (MyCellId, indexPath);

    // do whatever you need to with cell, such as assigning properties, etc.

    return cell;
  }
}

Esteja ciente, ao usar o novo padrão de reutilização com uma classe de célula personalizada, você precisa implementar o construtor que usa um IntPtr, como mostrado no trecho abaixo, caso contrário Objective-C , não será capaz de construir uma instância da classe de célula:

public class MyCell : UITableViewCell
{
  public MyCell (IntPtr p):base(p)
  {
  }
  ...
}

Você pode ver exemplos dos tópicos explicados acima no exemplo BasicTable vinculado a este artigo.

Adicionando um índice

Um índice ajuda o usuário a percorrer listas longas, normalmente ordenadas em ordem alfabética, embora você possa indexar por qualquer critério que desejar. O exemplo BasicTableIndex carrega uma lista muito maior de itens de um arquivo para demonstrar o índice. Cada item do índice corresponde a uma 'seção' da tabela.

A exibição do índice

Para dar suporte a 'seções', os dados por trás da tabela precisam ser agrupados, portanto, o exemplo BasicTableIndex cria um Dictionary<> a partir da matriz de cadeias de caracteres usando a primeira letra de cada item como a chave do dicionário:

indexedTableItems = new Dictionary<string, List<string>>();
foreach (var t in items) {
    if (indexedTableItems.ContainsKey (t[0].ToString ())) {
        indexedTableItems[t[0].ToString ()].Add(t);
    } else {
        indexedTableItems.Add (t[0].ToString (), new List<string>() {t});
    }
}
keys = indexedTableItems.Keys.ToArray ();

A UITableViewSource subclasse, em seguida, precisa dos seguintes métodos adicionados ou modificados para usar o Dictionary<> :

  • NumberOfSections – esse método é opcional, por padrão a tabela assume uma seção. Ao exibir um índice, esse método deve retornar o número de itens no índice (por exemplo, 26 se o índice contiver todas as letras do alfabeto inglês).
  • RowsInSection – retorna o número de linhas em uma determinada seção.
  • SectionIndexTitles – retorna a matriz de cadeias de caracteres que serão usadas para exibir o índice. O código de exemplo retorna uma matriz de letras.

Os métodos atualizados no arquivo de exemplo BasicTableIndex/TableSource.cs têm esta aparência:

public override nint NumberOfSections (UITableView tableView)
{
    return keys.Length;
}
public override nint RowsInSection (UITableView tableview, nint section)
{
    return indexedTableItems[keys[section]].Count;
}
public override string[] SectionIndexTitles (UITableView tableView)
{
    return keys;
}

Os índices geralmente são usados apenas com o estilo de tabela simples.

Adicionando cabeçalhos e rodapés

Cabeçalhos e rodapés podem ser usados para agrupar visualmente linhas em uma tabela. A estrutura de dados necessária é muito semelhante à adição de um índice – um Dictionary<> funciona muito bem. Em vez de usar o alfabeto para agrupar as células, este exemplo agrupará os vegetais por tipo botânico. A saída tem esta aparência:

Exemplos de cabeçalhos e rodapés

Para exibir cabeçalhos e rodapés, a UITableViewSource subclasse requer estes métodos adicionais:

  • TitleForHeader – retorna o texto a ser usado como cabeçalho
  • TitleForFooter – retorna o texto a ser usado como rodapé.

Os métodos atualizados no arquivo de exemplo BasicTableHeaderFooter/Code/TableSource.cs têm a seguinte aparência:

public override string TitleForHeader (UITableView tableView, nint section)
{
    return keys[section];
}
public override string TitleForFooter (UITableView tableView, nint section)
{
    return indexedTableItems[keys[section]].Count + " items";
}

Você pode personalizar ainda mais a aparência do cabeçalho e rodapé com um objeto View, usando as GetViewForHeader substituições do método and GetViewForFooter no UITableViewSource.