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”.

Immagine 1

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:

@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:

#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.

Immagine 4

Proprio come abbiamo fatto in precedenza cambiamo il nome della scheda in “Tabella”. Ecco il risultato finale:

Immagine 5

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:

Immagine 6

Ora dobbiamo solo collegare la classe alla tabella appena inserita. Dal Pannello dei Documenti (“Tools -> Reveal in Document Window”) navighiamo fino al seguente percorso:

Immagine 7

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”:

Immagine 8

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!

Immagine 9

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!

Immagine 10

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”.

Immagine 11

Possiamo spostare i file appena creati nella sezione “Classes” del nostro progetto, per avere un risultato come questo:

Immagine 12

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:

Immagine 11

Ora associamo questa vista alla sua classe. Dal Pannello dei Documenti selezioniamo il “File’s Owner” e nell'”Identity Inspector” selezioniamo “iPhoneDetailController” come classe:

Immagine 12

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:

Immagine 15

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:

Immagine 16

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:

#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:

// 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!!

Immagine 19

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.

Ingegnere informatico e sviluppatore freelance, mi occupo da anni di sviluppo per iOS (ma non solo). Dal 2008 scrivo su questo piccolo blog (con qualche lunga pausa), in cui parlo di programmazione e di qualsiasi altra cosa che mi diverta.

47 comments On DevTutorial #18 – TabBar Application, parte 2: inseriamo una NavigationBar

  • 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…

  • @luigi: ciao..
    detto così non saprei.. se vuoi inviami il progetto così provo a vedere se trovo l’inghippo..

  • @Andrea Busi: mi dai una tua mail? puoi anche scrivermi a marino_luigi@hotmail.com
    grazie

  • 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è?

  • @Nantas: controlla se nelle impostazioni del progetto è selezionata la versione corretta dell’SDK..

  • 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

  • @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 😉

  • 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

  • @Marco Taglioni: hai dimenticato di collegare il componente “view” in Interface Builder 😉

  • 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 🙁

  • @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 😉

  • 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

  • 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!!!

  • @iCiccio: certo, inviamelo pure a bubidevs at gmail dot com 😉

  • 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!!!!

  • 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???=(

  • 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

  • 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

  • @Max: no tranquillo, puoi anche inserirlo in uno xib diverso, anzi, forse è la soluzione più corretta 😉

  • 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

  • @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..

  • Complimenti per la guida completa!!
    Volevo chiedere una semplice domanda:
    Volevo implementare una bara di ricerca, questa guida va bene?
    http://www.devapp.it/wordpress/t041-–-usiamo-le-uitableview-parte-3-implementiamo-la-barra-di-ricerca.html
    Perché non riesco a farla funzionare..

    Grazie

  • @Luca: certamente si, vanno benissimo 😉

  • Ciao andrea,
    ho creato un lettore di xml, ho parsato xml, inserisco tutti i valori in una tabella con le celle personalizzate, adesso vorrei al tocco di una cella caricare il contenuto del mio xml ( immagine + grande, testo completo ). I valori da caricare sono gli stessi dell’xml di partenza.
    Da dove posso partire per creare utto questo?

    Grazie!!!

  • ciao andrea,
    ho provato a riseguire il tuo tutorial passo passo ma non visualizzo nessun elemento nella lista…

  • @pettedemon: ciao, strano.. sicuro di aver fatto tutto? se vuoi inviami il progetto via mail così provo a guardarci 😉

  • @pettedemon: devi parsarli tutti all’inizio.. ti predisponi una struttura dati che possa accogliere tutto, ad esempio (scritta in pseudo-linguaggio):

    classe Articolo {
       String *titolo;
       String *testo;
       Image *immagine;
    }

    poi alla selezione di una cella ti basterà recuperare l’oggetto che vuoi ed estrarne le informazioni, che però hai già scaricato e memorizzato 😉

  • Volevo dirti che sei un GRANDE!
    Grazie di tutte le guide…

  • Ciao, ho una domanda da fare, che può sembrare stupida ma non riesco ad arrivarci..

    Come faccio a collegare altre viste specifiche? Una diversa view per ogni cella della tabella.

    Grazie
    Matteo

  • @Matteo: esattamente come descritto dal punto 3 in poi.. così facendo devi crearti una vista per ogni cella della tabella.. in alternativa ti crei una sola classe (e una sola vista), con dei campi standard, che poi personalizzi via codice prima di visualizzare la vista di dettaglio..

  • @Andrea Busi: Ok, una volta che ho creato una view per ogni cella. come faccio a specificare quale view visualizzare con quella determitata cella. ho 15 celle con 15 view, che codice dovrei inserire nella tabellaController per richiamare una view specifica.

    cella 1 con view 1, cella 2 con view2, cella 3 con view 3 ecc
    Devo ripetere le stringhe di codice if ed else per ogni cella presente nella tableview?
    Quello che non riesco a capire è il collegamento.

    Punto 2: ho inserito la barra di ricerca, ma, nella selezione della cella ricercata, posta in prima posizione, (qualsiasi essa sia) mi apre la view iphoneDetail. al contrario se si trova in seconda posizione mi apre la otherDetail.

    Grazie

  • Ciao Andrea ho seguito la guida alla perfezione, tutto funziona, solo che quando clicco sulla prima riga a cui avevo associato un altra view l’app crasha! Soluzioni?

    PS è lo stesso errore di tonyangelo

  • Ciao. Volevo chiederti come faccio a collegare un singolo elemento della tabella con un altra tabella ? Per esempio , nel tuo programma appena clicco su iphone mi si apre una view con la sua foto. Se io al posto di quella view vorrei vedere una tabella con per esempio una lista di iOs supportati da iphone come faccio ?
    Grazie

  • @Francesco: la struttura rimane abbastanza invariata, dovrai solo creare una vista che, invece di contenere una semplice UIView, contenga una UITableView 😉

  • Come posso mettere in una vista un pageControl per fare uno slide di 12 foto?

  • Ciao, quando collego le viste a dettaglio, e monto sul simulatore mi da un errore di tipo SIGBRT.

    Cosa devo fare?

    Sono nelle tue mani, grazie,

    Alberto

  • @Alberto: ciao, solitamente è un errore legato alla memoria o ad una funzione invocata in maniera errata.. prova a fare un debug passo passo e dimmi quale istruzione ti da errore..

  • ma se io volessi fare:
    UITableView1–>UITableView2–>UIView
    cioè da uitableview1 vado alla uitableview2 e poi alla uiview , tutto selezionando una cella, basta ripetere quello che faccio per passare da uiTV1 a uiTV2 per uiTV2 a UIView ?

    perche come ho fatto io mi va in crash l’app quando cerco di visualizzare la uiview dicendomi:
    “warning: Unable to restore previously selected frame.”
    qualche soluzione?

  • Complimenti per i tutorial!!!!!!!!Bravo

  • Ciao,mi sono bloccato alla fine del punto 3.Quando faccio la simulazione non mi appare la lista dei nomi.Cosa sbaglio?Devo anche dichiarare qualcosa nel file delegate?Cè qualche punto che hai dato per scontato?Grazie

  • @Emanuele: controlla di aver fatto collegato il dataSource e il delegate con il File’s Owner della classe 😉

  • Ciao Andrea!
    Complimentissimi per gli articoli, mi sono stati molto utili per iniziare a capirci qualcosa 😉

    Volevo chiederti: se volessi cambiare il valore di ogni entry della tabella, in base al nome selezionato, come posso fare?
    In altre parole, io ho molte entries (più di 600), e, selezionandone una, vorrei che mi comparissero i dettagli di quella. Esempio: seleziono la voce “Italia”, mi si apre una pagina con i valori popolazione: x, forma di stato: x, forma di governo: y etc.

    Una sorta di database, che però cambia in base alla selezione, senza creare 800.000 xib, uno per ogni voce.

    Esiste il modo? 🙂

    Grazie 1000!

  • Salve a tutti, se vorrei fare in modo che quando premo su di un nome mi venga visualizzato un nuovo layout con tutte le caratteristiche, es ID, nome e cognome? Come posso fare? Ho letto i commenti, ma sono proprio all’inizio. Conosco le sintassi sql, ma non capisco cosa modificare all’interno del progetto… GRAZIE

  • @Francescog: ricavi il nome selezionato nella cella, esegui una nuova query al db e visualizzi i risultati in una vista creata ad-hoc 😉

  • ciao

    ho agiornato xcode ,e mi dice
    if (cell == nil){
    cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@”cellID”] autorelease];

    }
    init with frame:reuseldidentifier is deprecated nel file TabellaController.m
    ???
    come mai con il vecchio xcode andava bene

  • @aldo:
    alcune istruzioni con i nuovi SDK diventano deprecate, in funzione di altre che garantiscono risultati migliori o meno problemi.
    Per inizializzare la tua cella ti basta utilizzare la seguente istruzione:

            cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    

Leave a Reply to Francesco Cancel Reply

Your email address will not be published.

Site Footer