DevTutorial #18 – TabBar Application, parte 2: inseriamo una NavigationBar
Eccoci con il secondo tutorial dedicato alle TabBar. Nella prima lezione abbiamo visto come creare una struttura generale, composta da due sezioni. Oggi vedremo, invece, come inserire una UINavigationBar all’interno di una nuova sezione, e gestire il movimento tra le varie celle di una tabella. Ovvero, quando l’utente seleziona una determinata cella si aprirà una corrispondente vista che avremo definito con Interface Builder. La navigation bar, poi, ci permetterà di tornare alla tabella principale. Un po’ come avviene nel menù “Impostazioni” del’iPhone / iPod Touch!
Buon tutorial a tutti!
1. Creiamo un nuovo elemento per la TabBar
Iniziamo creando la classe che gestirà la nuova vista. Dal menù “File” scegliamo “New File…”, nel pannello che apparirà selezioniamo “UIViewController subclass” e chiamiamo questa nuova classe “TabellaController”.
Abbiamo già imparato a gestire una tabella in questa serie di tutorial, quindi non rispiegherò il codice utilizzato. Riutilizziamo gli stessi metodi, quindi inserite nel file “TabellaController.h” il seguente codice:
1 2 3 4 5 6 7 8 | @interface TabellaController : UITableViewController { //array che conterrà gli elementi da visualizzare nella tabella NSArray *lista; } @property (nonatomic, retain) NSArray *lista; @end |
Mentre in “TabellaController.m” inserite questi metodi:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #import "TabellaController.h" @implementation TabellaController @synthesize lista; - (void)awakeFromNib{ // creiamo la lista e inseriamo una serie di elementi da visualizzare nella nostra tabella lista = [[NSArray alloc] initWithObjects: @"iPhone", @"iPod", @"iPod Touch", @"iMac", @"iBook", @"MacBook", @"MacBook Pro", @"Mac Pro", @"PowerBook", nil]; } //setta il numero di sezioni della tabella - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ return 1; } //setta il numero di righe della tabella - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ //il numero di righe deve corrispondere al numero di elementi della lista return [lista count]; } //settiamo il contenuto delle varie celle - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellID"]; if (cell == nil){ cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"cellID"] autorelease]; } //inseriamo nella cello l'elemento della lista corrispondente cell.textLabel.text = [lista objectAtIndex:indexPath.row]; return cell; } |
Insomma, la classica gestione delle UITableView
Salviamo entrambi i file e riapriamo “MainWindow.xib” per tornare in Interface Builder.
2. Definiamo l’aspetto del nuovo elemento
Dalla Libreria prendiamo un componente UINavigationController e inseriamolo nella tab bar, proprio come abbiamo fatto nello scorso tutorial per le due viste.
Proprio come abbiamo fatto in precedenza cambiamo il nome della scheda in “Tabella”. Ecco il risultato finale:
Ora non ci resta che inserire la tabella all’interno di questa vista. Dalla Libreria prediamo un componente UITableViewController e inseriamolo nella nostra vista. Il risultato che dovete ottenere è il seguente:
Ora dobbiamo solo collegare la classe alla tabella appena inserita. Dal Pannello dei Documenti (“Tools -> Reveal in Document Window”) navighiamo fino al seguente percorso:
Come mostrato in figura, selezioniamo il componente “Table View Controller” (che non è altro che la nostra tabella) e apriamo l’”Identity Inspector”. Dal menù Class scegliamo “TabellaController”:
Abbiamo così concluso con la creazione della nostra tabella. Salviamo tutto, torniamo in XCode e clicchiamo su “Build and Go!”: la tabella sarà ora presente e funzionante nell’applicazione!
3. Implementiamo due viste di dettaglio
Ora vediamo di analizzare un aspetto che molti utenti mi hanno chiesto via mail. Se noi volessimo associare un determinato file “xib” (ovvero creato con Interface Builder) ad una cella, come potremmo fare? In questa seconda parte del tutorial vedremo proprio di analizzare i passaggi necessari. Andremo a definire due viste, una che conterrà una foto dell’elemento “iPhone”, mentre un’altra che avviserà l’utente dell’assenza di informazioni per un determinato prodotto. Ovviamente potreste realizzare una vista con i dettagli per ogni prodotto presente nella tabella, ma il meccanismo rimane invariato.
Iniziamo creando due nuovi file xib, dal menù “File -> New File…” e scegliendo “Empty XIB”. Io ho chiamato il primo file “iPhoneDetail” e il secondo “OtherDetail”, ma nulla vieta di chiamarli in modo diverso!
Procediamo proprio come abbiamo fatto all’inizio dello scorso tutorial per le viste “PrimaVista” e “Seconda Vista”, quindi definiamo subito via codice le due classi necessarie.
Andiamo, quindi, in “File -> New File…” e spostiamoci nella sezione “Cocoa Touch Class”, in cui selezioniamo il modello “UIViewController”: anche in questo caso dobbiamo creare due classi, chiamate “iPhoneDetailController” e “OtheDetailController”.
Possiamo spostare i file appena creati nella sezione “Classes” del nostro progetto, per avere un risultato come questo:
Ora siamo pronti per definire l’aspetto di queste due nuove viste.
4. Definiamo l’aspetto grafico delle due viste di dettaglio
Apriamo il file “iPhoneDetail.xib” in Interface Builder. Il procedimento è, come già detto, uguale a quello svolto per la definizione delle due viste “PrimaVista” e “SecondaVista”. Inseriamo, quindi, un componente UIView nel Pannello dei Documenti e modifichiamolo a nostro piacimento. Ecco come risulta essere la mia vista:
Ora associamo questa vista alla sua classe. Dal Pannello dei Documenti selezioniamo il “File’s Owner” e nell’”Identity Inspector” selezioniamo “iPhoneDetailController” come classe:
Andiamo poi in “Connections Inspector” e colleghiamo l’elemento “view” con la vista che abbiamo appena creato (quella contenente le due label per intenderci). Se abbiamo eseguito il passaggio in maniera corretta avremo questo risultato:
Abbiamo così completato la definizione della vista. Eseguiamo lo stesso procedimento anche per il file “OtherDetail.xib”, collegandola però alla classe “OtherDetailController”. Ecco come appare tale vista:
Possiamo salvare tutto e chiudere Interface Builder.
5. Come richiamare le due viste via codice
Ora non ci resta che analizzare il codice che ci permette di aprire queste due viste. Apriamo il file “TabellaController.h” e modifichiamolo nella seguente maniera:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #import <UIKit/UIKit.h> #import "iPhoneDetailController.h" #import "OtherDetailController.h" @interface TabellaController : UITableViewController { //array che conterrà gli elementi da visualizzare nella tabella NSArray *lista; //controller della vista che dovrà essere aperta UIViewController *detail; } @property (nonatomic, retain) NSArray *lista; @end |
Abbiamo per prima cosa importato le due classi delle viste (righe 2 e 3), e poi definito una vista generica (riga 10), che poi inizializzeremo con la classe “iPhoneDetailController” oppure “OtherDetailController”, a seconda del caso. Perchè abbiamo utilizzato “UIViewController” come tipo dell’elemento “detail”? Perchè abbiamo sfruttato un paradigma della programmazione ad oggetti, che ci permette di definire un elemento con una superclasse, per poi inizializzarlo con una sottoclasse più specifica.
Ora apriamo il file “TabellaController.m” e inseriamo il metodo che viene richiamato quando si clicca su una cella:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // Metodo relativo alla selezione di una cella - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.row == 0){ //l'utente ha cliccato sull'elemento iPhone, quindi carichiamo la vista relativa detail = [[iPhoneDetailController alloc] initWithNibName:@"iPhoneDetail" bundle:[NSBundle mainBundle]]; } else { detail = [[OtherDetailController alloc] initWithNibName:@"OtherDetail" bundle:[NSBundle mainBundle]]; } //Facciamo visualizzare la vista con i dettagli [self.navigationController pushViewController:detail animated:YES]; //rilasciamo il controller [detail release]; detail = nil; } |
Anche questo metodo lo avevamo già trovato nel tutorial dedicato alle tabelle. Analizziamo, però, il codice al suo interno. Troviamo inizialmente un ciclo if, che controlla se l’utente ha selezionato al prima cella, ovvero quella contenente l’elemento iPhone: se il controllo da esito positivo, inizializziamo l’elemento “detail” con la classe relativa alla vista “iPhoneDetail”, altrimenti con l’altra vista generica. La clausola “initWithNibName” si riferisce proprio al file xib che deve essere associato all’elemento “detail”.
Dopo il ciclo troviamo le istruzioni che ci permettono di far apparire la nuova vista; non preoccupatevi troppo, sono sempre queste istruzioni da utilizzare.
Abbiamo concluso!! Clicchiamo su “Build and Go!” e godiamoci la nostra applicazione funzionante!!
Se Avete Problemi, questo è il nostro file di progetto.
La guida è stata creata da Andrea Busi per “Bubi Devs”. I meriti, quindi, sono del legittimo autore.





























2 novembre 2009 alle 22:32
Salve,
ho seguito alla perfezione la Vs. guida ma non capisco perche non visualizzo la tabella, nemmeno quella di default ( ovviamente parlo nel momento in cui effettuo il build dell’applicazione )
Non riesco a capire dove sbaglio…
2 novembre 2009 alle 22:41
@luigi: ciao..
detto così non saprei.. se vuoi inviami il progetto così provo a vedere se trovo l’inghippo..
3 novembre 2009 alle 13:41
@Andrea Busi: mi dai una tua mail? puoi anche scrivermi a marino_luigi@hotmail.com
grazie
14 novembre 2009 alle 22:56
Stranamente, quando scarico il tuo progetto e provo a compilarlo, mi da un errore generale (there is non SDK with specified name or path “unknown path”).
Sapresti spiegarmi il perchè?
16 novembre 2009 alle 19:04
@Nantas: controlla se nelle impostazioni del progetto è selezionata la versione corretta dell’SDK..
25 febbraio 2010 alle 15:09
Ciao.
Qualcuno mi può spiegare come fare in modo che invece di una finestra di dettaglio mi si apra un’altra tabella?
Io seguo la guida e riesco a far apparire la tabella solo che non riesco a farci apparire dentro nessun elemento
25 febbraio 2010 alle 15:44
@Marco Taglioni: probabilmente non hai inserito la definizione della lista nel metodo esatto.. prova ad usare il metodo “viewWillAppear” e inizializzare li la lista con gli elementi
25 febbraio 2010 alle 15:50
2010-02-25 15:48:08.962 Assurdo![7239:207] *** Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘-[UIViewController _loadViewFromNibNamed:bundle:] loaded the “PrimaCategoria” nib but the view outlet was not set.’
questo è l’errore che ho.
la vista l’ho creata usando un UINavigationController con dentro un UITableViewController.
i
27 febbraio 2010 alle 11:54
@Marco Taglioni: hai dimenticato di collegare il componente “view” in Interface Builder
27 marzo 2010 alle 17:19
Grazie milla dell’ottima guida!
Io volevo capire se per mettere una tableview invece di una detailview come dice Marco, quando apro il nib devo prima mettere una uiview e poi sopra una tableview o direttamente una tableview?Grazie Andrea
Perchè io l’ho fatto e nella console non mi da neanche errori solo che appena seleziono la prima cella va in crash l’app
28 marzo 2010 alle 15:59
@tonyangelo: penso che basti inserire solo una UITableView.. se vuoi mandami il progetto via mail, così provo a vedere dove è l’errore e poi ti dico
2 aprile 2010 alle 14:25
ciao andrea,
innanzitutto complimenti per le ottime guide che scrivi!
avrei una domanda di pura curiosità: nel file TabellaController.m dopo aver rilasciato detail, lo setti a nil. perché fai questo? se l’hai rilasciato non dovrebbe essere sufficiente?
grazie mille!
pierre
24 maggio 2010 alle 22:34
CIao,
ho seguito questo tutorial per realizzare una mini applicazione per un mio sito, solo che quando clicco sulla riga va in crash, non capisco dove sta l’errore puoi aiutarmi? se è possibile ti mando il pacchetto completo via email e me lo verifichi magari potresti darmi anche qualche consiglio ciao!!!
26 maggio 2010 alle 23:34
@iCiccio: certo, inviamelo pure a bubidevs at gmail dot com
29 maggio 2010 alle 20:10
Ciao Andrea ho risolto da me, il mio problema adesso è che avrei una lista di diverse voci, tutte fanno capo ad un sto web quindi con una UIViewWeb sto creando le varie schermate, per intenderci le varie pagine del sito (Redazione, Chi siamo, collabora etc etc) ovviamente con questa parte di codice riesco a gestire se l’utente ha selezionato una riga della tabella, visto che sono circa 5 come devo implementare il codice di seguito riportato?
// Metodo relativo alla selezione di una cella
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0){
//l’utente ha cliccato sull’elemento iPhone, quindi carichiamo la vista relativa
detail = [[ChiSiamoController alloc] initWithNibName:@”ChiSiamo” bundle:[NSBundle mainBundle]];
} else {
detail = [[RedazioneController alloc] initWithNibName:@”Redazione” bundle:[NSBundle mainBundle]];
}
//Facciamo visualizzare la vista con i dettagli
[self.navigationController pushViewController:detail animated:YES];
//rilasciamo il controller
[detail release];
detail = nil;
}
Grazie infinite per il tuo aiuto, cmq ti mando via email il mio progettino, magari se me lo verifichi mi dici se sto procedendo correttamente oppure che alternativa ci sarebbe per la tipologia di applicazione che voglio creare (gratuita), ti scrivo tutto nella mail.
ciao!!!!
6 giugno 2010 alle 19:39
ciao andrea…Seenti…io Avrei bisogno di creare una di app tab-bar…con 1 vista e 2 tabelle ho bisogno…ma intanto stavo provando con una…come mai ho fatto tutti i passaggi,ho collegato tutto ma quando lancio l’app mi mostra prodotti apple in alto, ma la tabella vuota???=(
10 giugno 2010 alle 18:18
Ciao, scusate io avrei un piccolo problemino…prendendo spunto da questa guida ho ricreato un tap bar con tabella solo che ho diviso la lista degli elementi in sezioni..(creando ovviamente apposite array e dict)…fin qui tutto bene…ho diviso le sezioni utilizzando uno Switch creando appunto 4 “case”….
adesso dovrei associare vari file xib(in modo da creare piu finestre) alle relative celle…ho utilizzato questa stringa :
if (indexPath.row == 0){
//l’utente ha cliccato sull’elemento iPhone, quindi carichiamo la vista relativa
detail = [[iPhoneDetailController alloc] initWithNibName:@”iPhoneDetail” bundle:[NSBundle mainBundle]];
} else …
aggiungendone tante quante le celle…scoprendo pero il problema…il row indicato(che sia 0,1,2) non si riferisce alla prima,seconda ecc, cella, ma ALLA 1A CELLA DI OGNI SEZIONE….associandomi quindi 1 xib per per ogni 1 cella di ogni sezione…allora ho fatto una modifica….che pero mi si è rivoltata contro:
if (indexPath.section == 0, indexPath.row == 0){
detail = [[NOME FIEL XIB alloc] initWithNibName:@”NOME FIEL XIB” bundle:[NSBundle mainBundle]];
} else if (indexPath.section == 0, indexPath.row == 1 ){
detail = [[NOME FIEL XIB alloc] initWithNibName:@”NOME FIEL XIB” bundle:[NSBundle mainBundle]];
in questo modo ho pensato che associasse il determinato xib alla SEZIONE da me indicata e alla CELLA da me indicata…mi associa quest’ordine a tutte le sezioni….credo sicuramente di aver sbagliato……HELP
PS.scusate la mia “scrittura”….help help help
3 agosto 2010 alle 16:16
ciao. mi chiedevo se è possibile inserire il terzo tab in un nib separato o se è per forza necessario inserirlo all’interno di quello principale.
mi sembra che separarlo sia meglio dal punto di vista architetturale
4 agosto 2010 alle 11:27
@Max: no tranquillo, puoi anche inserirlo in uno xib diverso, anzi, forse è la soluzione più corretta
2 settembre 2010 alle 10:57
Salve a tutti Andrea ho bisogno di aiuto non riesco a far girare l’applicazione mi dice errore
error: There is no SDK with the name or path ‘iphoneos3.0′
non so piu che fare grazie mille ti posso contattare per mail eventualmente
2 settembre 2010 alle 19:23
@help: ciao, c’è qualcosa nelle proprietà progetto da modificare.. controlla che sia settato tutto su iOS 4, casomai se non riesci inviami pure il progetto via mail..