Iniziamo a dare forma al nostro ListViewController
(che ricordiamo mostrerà i dati inseriti) inserendo un nuovo oggetto: l'UITableView
. Quest'ultimo altro non è che una tabbella e risulta essere uno degli oggetti maggriormente utilizzato nello sviluppo di applicazioni iOS. Risulta anche essere uno strumento molto potente in quanto, in maniera relativamente semplice, avremo la possibilità di mostrare anche un grande numero di informazioni in maniera ordinata.
Struttura di un oggetto UITableViewCell
Ogni singola entry della tabella, che mostrerà le informazioni di un singolo elementi, è un oggetto di tipo UItableViewCell
opportunamente creato. I principali attributi che sfrutteremo, appartanenti alla classe UITableViewCell
, sono i seguenti:
textLabel
: è una label nella quale inserire il contenuto principale di una cella e che nel nostro caso conterrà il valore associata allaproperty name
dell'oggettoFruit
.detailTextLabel
: è una label aggiuntiva (e che avrà un font di dimensione inferiore rispetto alla precedente label) di una cella e che nel nostro caso conterrà il valore associata allaproperty origin
dell'oggettoFruit
.imageView
: è un oggetto di tipoUIImageView
che conterrà l'immagine della cella e che nel nostro caso sarà uguale allaproperty image
dell'oggettoFruit
.
I contenuti all'interno di una tabella sono organizzati gerarchicamente utilizzando le sezioni e le righe. L'oggetto NSIndexPath
racchiude per ogni elemento della tabella, la sua sezione e la sua riga offrendo così un indice univoco. Anche la TableView, oltre ad avere un delegate come gli oggetti UITextField
e UITextView
precedentemente incontrati, possiede anche un data source ovvero una classe che fornirà alla tabella i dati da impaginare; la classe data source dovrà implementare il protocollo UITableViewDataSource
mettendo così a disposizione dello sviluppatore dei metodi che consentiranno il popolamento della tabella.
Implementiamo la tabella nella classe ListViewController
Fatta questa breve introduzione teorica vediamo come inserire la tabella nella nostra applicazione. Come prima cosa, dato che dobbiamo impaginare le informazioni immagazzinate in oggetti di tipo Fruit
è necessario definire delle property
sugli attributi della classe per concederne l'accesso. Andiamo quindi nel file Fruit.h
ed inseriamo le seguenti property
:
@property (nonatomic,strong) NSString* name;
@property (nonatomic,strong) NSString* origin;
@property (nonatomic,strong) NSString* description;
@property (nonatomic,strong) UIImage* image;
Spostiamoci poi nel file Fruit.m ed effettuiamo il synthesize
delle property
:
@synthesize name = _name;
@synthesize origin = _origin;
@synthesize description = _description;
@synthesize image = _image;
Concluse queste operazioni iniziali andiamo a dichiarare un oggetto di tipo UITableView
all'interno del file ListViewController.h:
UITableView *_tableView;
Passiamo poi alla sua allocazione all'interno del metodo initWithNibName: boundle:
nel seguente modo:
_tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 0, 320, 480) style:UITableViewStylePlain];
_tableView.dataSource = self;
_tableView.delegate = self;
[self.view addSubview:_tableView];
Come possiamo vedere, nell'inizializzazione della tabella, abbiamo anche definito il suo style
; i due tipi di stile associabili alla tabella sono UITableViewStylePlain
e UITableViewStyleGrouped
(vedremo in seguito le differenze tra le rese grafiche dei due stili). Abbiamo poi scelto di associare come dataSource
e come delegate
la nostra stessa classe. Nel file ListViewController.h
andiamo ad implementare il protocollo UITableViewDataSource
e il protocollo UITableViewDelegate
nel seguente modo:
@interface ListViewController : UIViewController <InsertDelegate, UITableViewDataSource,UITableViewDelegate>
Adesso abbiamo a dispozizione i metodi necessari per poter impaginare i dati all'interno della tabella. I metodi obbligatori per poter correttamente mostrare a schermo delle informazioni all'interno della tabella sono i seguenti:
numberOfSectionsInTableView:
: è il metodo che ritorna il numero di sezioni possedute dalla tabellatableView: numberOfRowsInSection:
: è il metodo che, dato l'indice di una sezione, ritorna il numero di righe associate a quella sezionetableView: cellForRowAtIndexPath:
: è il metodo che, dato un oggetto di tipoNSIndexPath
(quindi l'indice univoco per l'elemento della tabella), si occupa di mostrare i dati di un elemento.
Tali metodi verranno invocati in maniera automatica quando la tabella verrà istanziata. Andiamo adesso ad implementare tali metodi. Spostiamoci nel file ListViewController.m ed inseriamo il seguente codice:
- (int)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
- (int)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [_itemsList count];
}
Al primo metodo facciamo tornare valore 1 perchè vogliamo una sola sezione per la nostra tabella. Al secondo metodo facciamo tornare, come numero di righe, la lunghezza dell'array _itemsList
contenente gli oggetti Fruit.h creati nella classe InsertViewController
.
Adesso mostriamo il codice relativo al metodo che si occupa del popolamento dei dati all'interno della tabella:
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [[_itemsList objectAtIndex:indexPath.row]name];
cell.detailTextLabel.text = [[_itemsList objectAtIndex:indexPath.row]origin];
cell.imageView.image = [[_itemsList objectAtIndex:indexPath.row]image];
return cell;
}
Prima di analizzare il codice è opportuno fare una breve panoramica sul funzionameto della creazione delle celle. Il funzionamento standard di una tabella è quello di allocare in memoria tanti oggetti UITableViewCell
quante sono le celle visibili della tabella nello schermo del dispositivo. Tutte le volte che l'utente effettua uno scroll della tabella, in automatico viene richiamo il metodo tableView: cellForRowAtIndexPath:
che non andrà a creare altri oggetti di tipo UITableViewCell
, ma bensì utilizzerà quelli precedentemente creati andando solamente a cambiarne il contenuto.
Passando al codice, per implementare questo comportamento, definiamo un CellIdentifier
di tipo NSString
che verrà utilizzato per l'acquisizione della cella usando il metodo dequeueReusableCellWithIdentifier
che implementerà la politica di riuso degli oggetti cella. Con quel metodo viene ritornato un oggetto di tipo UITableViewCell
utilizzando il CellIdentifier
precedentemente creato e se la cella risulta uguale a nil
(quindi la cella non è ancora stata creata) la allochiamo e ne inseriamo il contenuto adando a reperire le informazioni all'interno dell'array _itemsList
utilizzando l'indice univoco IndexPath.row
(ovvero il numero di riga con valore della sezione pari ad 1).
Quando l'utente effettuerà uno scroll della tabella la cella ritornata dal meotodo dequeueReusableCellWithIdentifier
sarà una cella valida e quindi il flusso d'esecuzione non entrerà all'interno del blocco condizionale if
e verrà effettuato solo un aggiornamento dei contenuti.
Adesso l'infrastruttura necessaria per il corretto funzionamento della tabella è terminata. Dobbiamo però aggiungere sempre nel file ListViewController.m
all'interno del metodo insertNewFruit:
(dopo l'aggiunta del nuovo oggetto Fruit
nell'array) la seguente linea di codice:
[_tableView reloadData];
Questa chiamata è necessaria perchè, come abbiamo detto prima, i metodi per il popolamento dei dati vengono chiamati quando la tabella viene allocata e l'array dei frutti inseriti ha lunghezza zero. E' dunque necessario, dopo ogni inserimento, invocare il metodo reloadData
sulla tabella per aggiornare i dati mostrati.
Finalmente, effettaundo un Run del progetto (dopo l'inserimento di qualche frutto), avremo il seguente risultato: