T#041 – Usiamo le UITableView (parte 3): Implementiamo la barra di ricerca
Siamo giunti al terzo appuntamento riguardante le UITableView, ovvero le tabelle presenti nell’SDK per iPhone. Negli scorsi tutorial abbiamo visto come creare e popolare una tabella (Parte I) e come aggiungere alcune funzionalità, come la possibilità di editare (nel nostro caso cancellare) alcune celle e intercettare il tocco dell’utente (Parte II). In questa nuova parte vedremo, invece, come aggiungere una funzionalità molto importante: la ricerca.
1. Inseriamo la barra di ricerca
Facciamo doppio clic sul file “RootViewController.xib” per aprire IB, e inseriamo la nostra barra di ricerca. Anche in questo caso, grazie al firmware 3.0, sono state apportate delle migliorie nelle API che gestiscono la ricerca nelle tabelle. Dalla Libreria selezioniamo il componente “Search Bar and Search Display Controller” e trasciniamolo sopra l’elemento “Table View” che vedete nel Pannello dei documenti.

Se avete eseguito tutto correttamente la vostra tabella mostrerà ora anche una barra di ricerca:


Ancora una volta davvero tutto molto semplice!
Salvate tutto e chiudere Interface Buider.
2. Modifichiamo i metodi già esistenti
Prima di implementare la ricerca vera e propria, dobbiamo fare delle piccole modifiche ai metodi già presenti nel nostro progetto.
Iniziamo aprendo il file “RootViewController.h” e completiamo così la definizione della classe:
1 2 3 4 5 6 7 8 9 | @interface RootViewController : UITableViewController { NSMutableArray *lista; NSMutableArray *filteredListContent; } @property (nonatomic, retain) NSMutableArray *lista; @property (nonatomic, retain) NSMutableArray *filteredListContent; @end |
Abbiamo semplicemente dichiarato una nuova lista (riga 3), che conterrà gli elementi “filtrati”, ovvero quelli che corrispondono al criterio di ricerca.
Ora dobbiamo modificare i metodi che si occupano dell’inizializzazione della tabella. Iniziamo dal metodo “viewDidLoad” e dalla funzione “@synthetize”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #import "RootViewController.h" @implementation RootViewController @synthesize lista, filteredListContent; - (void)viewDidLoad { [super viewDidLoad]; self.title = @"Prodotti Apple"; //elementi da visualizzare nella tabella lista = [[NSMutableArray alloc] initWithObjects:@"iPhone", @"iPod", @"iPod Touch", @"iMac", @"iBook", @"MacBook", @"MacBook Pro", @"Mac Pro", @"PowerBook", nil]; // Uncomment the following line to display an Edit button in the navigation bar for this view controller. self.navigationItem.rightBarButtonItem = self.editButtonItem; // crea la lista filtrata, inizializzandola con il numero di elementi dell'array "lista" filteredListContent = [[NSMutableArray alloc] initWithCapacity: [lista count]]; //inserisce in questa nuova lista gli elementi della lista originale [filteredListContent addObjectsFromArray: lista]; } |
Come potete vedere la prima parte del metodo “viewDidLoad” è uguale a quella che già abbiamo scritto nei precedenti tutorial. Alle righe 21 e 23, invece, abbiamo definito due istruzioni, che hanno il compito di inizializzare il nuovo array, che ci servirà nell’algoritmo di ricerca. Inizialmente questa lista coinciderà con quella degli elementi iniziali, successivamente verrà modificata durante la digitazione dell’utente della stringa di ricerca.
Il secondo metodo da modificare è “numberOfRowsInSection”. Ecco il nuovo codice:
1 2 3 4 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ //il numero di righe deve corrispondere al numero di elementi della lista return [filteredListContent count]; } |
Questa modifica va eseguita in quanto la tabella ora non è più composta dagli elementi della lista originale, ma da quelli della lista filtrata, ovvero da quegli elementi selezionati mediante la ricerca. Ovviamente se l’utente non esegue nessuna ricerca, gli elementi della lista filtrata corrisponderanno agli elementi della lista originale.
Se vi è chiaro questo ragionamento, è facile intuire quali saranno i prossimi metodi da modificare: il primo è “cellForRowAtIndexPath:”, ovvero il metodo che si occupa di inserire i valori nelle celle. Modificate l’ultima istruzione al suo interno nella seguente maniera:
1 2 | //inseriamo nella cello l'elemento della lista corrispondente cell.textLabel.text = [filteredListContent objectAtIndex:indexPath.row]; |
Modifichiamo, poi, il metodo “commitEditingStyle” modificando l’istruzione presente alla riga 4:
1 2 3 4 5 6 7 8 | //controlla se l'azione compiuta è un'eliminazione if (editingStyle == UITableViewCellEditingStyleDelete) { //elimina l'elemento dalla lista [filteredListContent removeObjectAtIndex:indexPath.row]; //elimina le'elemento dalla tabella [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; } |
L’ultimo metodo da modificare, infine, è “didSelectRowAtIndexPath:” e anche in questo caso l’unica modifica riguarda proprio la lista di riferimento:
1 2 3 4 5 6 7 | // Se selezioniamo una riga appare un pop-up con l'elemento in questione - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { UIAlertView *popUp = [[UIAlertView alloc] initWithTitle:@"Hai selezionato:" message:[filteredListContent objectAtIndex:indexPath.row] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [popUp show]; [popUp release]; } |
Abbiamo così eseguito tutte le modifiche necessarie!
3. Implementiamo la ricerca
È arrivato il momento di implementare la ricerca vera e propria. Prima di mostrarvi i passaggi necessari, devo premettere che il codice non è stato scritto da me, ma l’ho preso da un esempio realizzato dalla stessa Apple. I commenti, quindi, saranno davvero pochi, anche perchè non è fondamentale capire come funziona tale algoritmo, in quanto lo stesso codice si può utilizzare in qualsiasi altra applicazione che necessiti di una ricerca.
Iniziate inserendo questi due metodi:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString{ [self filterContentForSearchText:searchString scope: [[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]]; // Return YES to cause the search result table view to be reloaded. return YES; } - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption{ [self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope: [[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]]; // Return YES to cause the search result table view to be reloaded. return YES; } |
I due metodi appena mostrati fanno parte del delegato “UISearchDisplayController”, che si occupa di gestire in maniera corretta la barra di ricerca.
Dobbiamo inserire, inoltre, un metodo che si occupa di reinserire tutti gli elementi originali nell’array filtrato quando l’utente annulla la ricerca. Per fare ciò ci serviremo del metodo “searchBarCancelButtonClicked”, che vieni richiamato proprio quando l’utente annulla la ricerca tramite il bottone “Cancel”:
1 2 3 4 | - (void)searchBarCancelButtonClicked:(UISearchBar *)saearchBar { [self.filteredListContent removeAllObjects]; [self.filteredListContent addObjectsFromArray: lista]; } |
Senza questo metodo l’applicazione crasha quando si annulla una ricerca e si seleziona un elemento che è superiore al numero di elementi dell’ultima ricerca (grazie a Dario per la segnalazione e ad Andrea per la soluzione nei commenti).
Ora dobbiamo inserire l’algoritmo di ricerca vero e proprio. Eccovelo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | - (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope{ /* Update the filtered array based on the search text and scope. */ [self.filteredListContent removeAllObjects]; // First clear the filtered array. /* Search the main list for products whose type matches the scope (if selected) and whose name matches searchText; add items that match to the filtered array. */ NSString *cellTitle; for (cellTitle in lista){ NSComparisonResult result = [cellTitle compare:searchText options:NSCaseInsensitiveSearch range:NSMakeRange(0, [searchText length])]; if (result == NSOrderedSame){ [filteredListContent addObject:cellTitle]; } } } |
Per questo algoritmo non voglio spendere parole, in quanto, come detto in precedenza, è stato scritto da Apple, quindi non posso permettermi di fare modifiche!
Cliccate ora su “Build and Go!”, se avete eseguito tutto correttamente si aprirà la vostra applicazione!


















Oh bene.. cercavo proprio questo!! vediamo se riesco a far funzionare il tutto..