Popolamento di una tabella con dati in Xamarin.iOS

Per aggiungere righe a un oggetto UITableView è necessario implementare una UITableViewSource sottoclasse ed eseguire l'override dei metodi chiamati dalla vista tabella per popolare se stesso.

Questa guida illustra:

  • Creazione di sottoclassi di un oggetto UITableViewSource
  • Riutilizzo cella
  • Aggiunta di un indice
  • Aggiunta di intestazioni e piè di pagina

Sottoclasse UITableViewSource

A UITableViewSource ogni sottoclasse viene assegnata una UITableViewsottoclasse . La vista tabella esegue una query sulla classe di origine per determinare come eseguire il rendering, ad esempio il numero di righe necessarie e l'altezza di ogni riga, se diversa dall'impostazione predefinita. Soprattutto, l'origine fornisce ogni visualizzazione cella popolata con i dati.

Per visualizzare una tabella sono necessari solo due metodi obbligatori:

  • RowsInSection : restituisce un nint conteggio del numero totale di righe di dati che la tabella deve visualizzare.
  • GetCell : restituisce un UITableViewCell oggetto popolato con dati per l'indice di riga corrispondente passato al metodo .

Il file di esempio BasicTable TableSource.cs include l'implementazione più semplice possibile di UITableViewSource. È possibile vedere nel frammento di codice seguente che accetta una matrice di stringhe da visualizzare nella tabella e restituisce uno stile di cella predefinito contenente ogni stringa:

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;
        }
}

Un UITableViewSource oggetto può usare qualsiasi struttura di dati, da una semplice matrice di stringhe (come illustrato in questo esempio) a una raccolta List <> o un'altra raccolta. L'implementazione dei UITableViewSource metodi isola la tabella dalla struttura di dati sottostante.

Per usare questa sottoclasse, creare una matrice di stringhe per costruire l'origine e quindi assegnarla a un'istanza di 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);
}

La tabella risultante è simile alla seguente:

Tabella di esempio in esecuzione

La maggior parte delle tabelle consente all'utente di toccare una riga per selezionarla ed eseguire altre azioni (ad esempio la riproduzione di un brano o la chiamata a un contatto o la visualizzazione di un'altra schermata). A tale scopo, è necessario eseguire alcune operazioni. Creare prima di tutto un AlertController per visualizzare un messaggio quando l'utente fa clic su una riga aggiungendo quanto segue al RowSelected metodo :

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);
}

Creare quindi un'istanza del controller di visualizzazione:

HomeScreen owner;

Aggiungere un costruttore alla classe UITableViewSource che accetta un controller di visualizzazione come parametro e lo salva in un campo:

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

}

Modificare il metodo ViewDidLoad in cui viene creata la classe UITableViewSource per passare il this riferimento:

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

Infine, tornare al RowSelected metodo, chiamare PresentViewController sul campo memorizzato nella cache:

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

    ...
}

Ora l'utente può toccare una riga e verrà visualizzato un avviso:

Avviso selezionato per la riga

Riutilizzo cella

In questo esempio sono presenti solo sei elementi, quindi non è necessario riutilizzare le celle. Quando si visualizzano centinaia o migliaia di righe, tuttavia, si tratta di uno spreco di memoria per creare centinaia o migliaia di UITableViewCell oggetti quando solo pochi si adattano allo schermo alla volta.

Per evitare questa situazione, quando una cella scompare dallo schermo, la visualizzazione viene inserita in una coda per il riutilizzo. Quando l'utente scorre, la tabella chiama GetCell per richiedere la visualizzazione di nuove visualizzazioni, per riutilizzare una cella esistente (attualmente non visualizzata) semplicemente chiamare il DequeueReusableCell metodo . Se una cella è disponibile per il riutilizzo verrà restituita, in caso contrario viene restituito un valore Null e il codice deve creare una nuova istanza della cella.

Questo frammento di codice dell'esempio illustra il modello:

// 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);

Crea cellIdentifier in modo efficace code separate per diversi tipi di cella. In questo esempio tutte le celle hanno lo stesso aspetto, quindi viene usato un solo identificatore hardcoded. Se sono presenti tipi diversi di cella, ognuno deve avere una stringa di identificatore diversa, sia quando viene creata un'istanza che quando vengono richieste dalla coda di riutilizzo.

Riutilizzo delle celle in iOS 6+

iOS 6 ha aggiunto un modello di riutilizzo delle celle simile a quello dell'introduzione alle visualizzazioni raccolta. Anche se il modello di riutilizzo esistente illustrato in precedenza è ancora supportato per la compatibilità con le versioni precedenti, questo nuovo modello è preferibile perché rimuove la necessità del controllo Null nella cella.

Con il nuovo modello un'applicazione registra la classe cella o xib da usare chiamando RegisterClassForCellReuse o RegisterNibForCellReuse nel costruttore del controller. Quindi, quando si dequeue la cella nel GetCell metodo , è sufficiente chiamare DequeueReusableCell passando l'identificatore registrato per la classe cella o xib e il percorso di indice.

Ad esempio, il codice seguente registra una classe cella personalizzata in un UITableViewController:

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

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

Con la classe MyCell registrata, la cella può essere dequeuata nel GetCell metodo di senza la necessità del UITableViewSource controllo Null aggiuntivo, come illustrato di seguito:

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;
  }
}

Tenere presente che, quando si usa il nuovo modello di riutilizzo con una classe di celle personalizzata, è necessario implementare il costruttore che accetta un oggetto IntPtr, come illustrato nel frammento di codice seguente, altrimenti Objective-C non sarà in grado di costruire un'istanza della classe cella:

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

È possibile visualizzare esempi degli argomenti illustrati in precedenza nell'esempio BasicTable collegato a questo articolo.

Aggiunta di un indice

Un indice consente all'utente di scorrere elenchi lunghi, in genere ordinati alfabeticamente, anche se è possibile indicizzare in base ai criteri desiderati. L'esempio BasicTableIndex carica un elenco di elementi molto più lungo da un file per illustrare l'indice. Ogni elemento nell'indice corrisponde a una "sezione" della tabella.

Visualizzazione indice

Per supportare le "sezioni" i dati dietro la tabella devono essere raggruppati, quindi l'esempio BasicTableIndex crea un oggetto Dictionary<> dalla matrice di stringhe usando la prima lettera di ogni elemento come chiave del dizionario:

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 ();

La UITableViewSource sottoclasse richiede quindi i metodi seguenti aggiunti o modificati per usare :Dictionary<>

  • NumberOfSections : questo metodo è facoltativo, per impostazione predefinita la tabella presuppone una sezione. Quando si visualizza un indice, questo metodo deve restituire il numero di elementi nell'indice, ad esempio 26 se l'indice contiene tutte le lettere dell'alfabeto inglese.
  • RowsInSection : restituisce il numero di righe in una determinata sezione.
  • SectionIndexTitles : restituisce la matrice di stringhe che verranno usate per visualizzare l'indice. Il codice di esempio restituisce una matrice di lettere.

I metodi aggiornati nel file di esempio BasicTableIndex/TableSource.cs hanno un aspetto simile al seguente:

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;
}

Gli indici vengono in genere usati solo con lo stile di tabella Plain.

Aggiunta di intestazioni e piè di pagina

Le intestazioni e i piè di pagina possono essere usati per raggruppare visivamente le righe in una tabella. La struttura dei dati necessaria è molto simile all'aggiunta di un indice. Un funziona Dictionary<> davvero bene. Invece di usare l'alfabeto per raggruppare le celle, questo esempio raggruppa le verdure per tipo botanico. L'output sarà simile al seguente:

Intestazioni e piè di pagina di esempio

Per visualizzare intestazioni e piè di pagina, la UITableViewSource sottoclasse richiede questi metodi aggiuntivi:

  • TitleForHeader : restituisce il testo da usare come intestazione
  • TitleForFooter : restituisce il testo da usare come piè di pagina.

I metodi aggiornati nel file di esempio BasicTableHeaderFooter/Code/TableSource.cs hanno un aspetto simile al seguente:

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";
}

È possibile personalizzare ulteriormente l'aspetto dell'intestazione e del piè di pagina con un oggetto View, usando gli override del GetViewForHeader metodo e GetViewForFooter in UITableViewSource.