Storyboards in iOS 5 e Xcode 4.2 – Le basi (Parte 2)

Abbiamo lasciato la prima parte con l’idea di costruire il seguente schema:

storyboards

L’aggiunta di un Table View controller

Le due scene che sono attualmente collegate al tabBar Controller sono entrambe UIViewControllers regolari. Voglio sostituire invece la scena dalla prima scheda con un UITableViewController.

Fare clic su tale firstView Controller per selezionarlo e quindi eliminarlo. Dalla libreria degli oggetti trascinare un nuovo Table View controller nella tela:

xcode storyboard

Con il Table View controller selezionato scegliere Editor -> Embed In -> Navigation controller da menu di Xcode.

xcode storyboard

Poiché il controller di navigazione è anche un controller vista (proprio come il tabBar controller), ha una relazione che punta al controller Table View. È anche possibile vedere queste relazioni nella struttura del documento:

xcode storyboard

Si noti che l’inserimento della Table View Controller ha dato una barra di navigazione. L’editor di storyboard automaticamente l’ha messo lì perché questa scena sarà ora visualizzato all’interno della struttura Navigation controller. Non è un oggetto UINavigationBar reale, ma una simulazione. Se guardate l’ispettore Attributi per il controller Table View, potrai vedere la sezione “Simulated metrics” in alto:

xcode storyboard

“Inferred” è l’impostazione predefinita per storyboard e significa che la scena mostrerà una barra di navigazione quando è all’interno di un controller di navigazione, una barra delle schede quando è all’interno di un tabBar Controller e così via. Si può ignorare queste impostazioni se si vuole, ma bisogna tenere presente che sono qui solo per aiutare a progettare le schermate. La metrica simulata non viene usata durante il runtime, sono solo un aiuto di visual design che mostra ciò che il vostro schermo finisce per somigliare.

Colleghiamo queste nuove scene al nostro controller Tab Bar. Ctrl-trascinare dal controller TabBar al controller di navigazione:

xcode storyboard

Quando lasciamo compare un piccolo menu a comparsa:

xocde storyboard

Scegli “Relationship – view controllers”. Questo crea una nuova relazione tra le due scene.

xcode storyboard

Il TabBar controller ha due relazioni, una per ogni scheda. Il navigation controller ha una relazione di connessione con il TableView Controller. C’è però un altro tipo di relazione, il “segue”, di cui parleremo più tardi.

Quando abbiamo fatto questa nuova connessione, una nuova scheda è stata aggiunta alla barra delle schede controller, chiamata semplicemente “Item”. Voglio che questo nuovo scenario sia la prima scheda, quindi dobbiamo trascinare le schede per cambiare il loro ordine:

xcode storyboard

Eseguiamo l’applicazione e proviamo.La prima scheda contiene ora una visualizzazione della tabella all’interno di un controller di navigazione.

xcode storyboard

Prima di dare effettiva funzionalità all’applicazione cerchiamo di “pulire” un po lo storyboard.

Prima di mettere un po ‘effettiva funzionalità in questa applicazione, cerchiamo di ripulire un po’ lo storyboard. Voglio chiamare la prima scheda “Giocatori” e il secondo “Gesti”. Non si modifica questo sul controller barra delle schede in sé, ma in vista controllori che sono collegati a queste schede.Non appena si collega un viewController al tabBar Controller si seleziona la scheda. È possibile utilizzare la barra dei Tab per configurare il titolo della scheda e l’immagine.

xcode storyboard

Dovremmo anche mettere alcune immagini in queste schede.

Eseguiamo l’applicazione e proviamo:

xcode storyboard

Prototipi di Celle

Avrete notato che da quando abbiamo aggiunto il Controller Table View, Xcode ha iniziato a lamentarsi:

xode storyboard

Quando si aggiunge un controller Table View ad uno storyboard, vuole usare le celle prototipo di default ma non le abbiamo ancora configurato correttamente, di qui il warning.

Le celle prototipo sono uno dei vantaggi interessanti che offrono le storyboard rispetto ai nib regolari. In precedenza, se si voleva utilizzare una cella della tabella con un design personalizzato bisognava aggiungere il vostro subviews alla cella via codice, o creare un nuovo nib specifico per quella cella e poi caricarlo dal nib con qualche magia. Che è ancora possibile, ma le celle prototipo possono rendere le cose molto più facili per voi. Ora è possibile progettare le celle direttamente nell’editor storyboard.

Il controller Table View è dotato di un prototipo di cella vuota. Clicca su quella cella e selezionala e nell’attribute inspector seleziona lo stile come subtitle. Questo immediatamente cambierà l’apparenza della cella includendo due label. Se avete mai creato i prototipi via codice sicuramente riconoscerete questo come lo stile “UITableViewCellStyleSubtitle“. Potete sia scegliere uno stile predefinito oppure crearne uno vostro, che è proprio quello che faremo.

xcode story

Aggiungiamo un nuovo file al progetto. Scegliamo tra i modelli “UIViewController subclass” e chiamiamolo PlayersViewController della sottoclasse UITableViewController. Deselezioniamo l’opzione “with XIB” in quanto il nostro design già è presente nello storyboard. Niente NIB per oggi!!

xocde storyboard

Torna indietro nell’editor dello storyboard e seleziona oò Table View controller. Nell’identity inspector imposta la classe a “PlayersViewController” questo è un passo essenziale per la popolazione della tabella. Non dimenticare questo passaggio oppure la classe creata non verrà usata.

xcode storyboard

Adesso per la prima volta è arriva l’ora di scrivere un po di codice. Spostiamoci nel file PlayersViewController.h:

#import <UIKit/UIKit.h></pre>
<pre>@interface PlayersViewController : UITableViewController</pre>
<pre>@property (nonatomic, strong) NSMutableArray *players;</pre>
<pre>@end

Questo array conterrà tutti i dati dell’applicazione. Creiamo una nuova classe chiamata Player come sottoclasse di NSObject. Spostiamoci nel file Player.h e modifichiamolo come segue:

@interface Player : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *game;
@property (nonatomic, assign) int rating;

@end

e nel file Player.m

#import "Player.h"

@implementation Player

@synthesize name;
@synthesize game;
@synthesize rating;

@end

niente di speciale fin qui. Player è un semplice contenitore con tre proprietà: il nome, il gioco e la valutazione (da 1 a 5 stelle).

Nell’AppDelegate.m importiamo la classe ed il videwController:

#import "AppDelegate.h"
#import "Player.h"
#import "PlayersViewController.h"

@implementation AppDelegate {
	NSMutableArray *players;
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
	players = [NSMutableArray arrayWithCapacity:20];
	Player *player = [[Player alloc] init];
	player.name = @"Bill Evans";
	player.game = @"Tic-Tac-Toe";
	player.rating = 4;
	[players addObject:player];
	player = [[Player alloc] init];
	player.name = @"Oscar Peterson";
	player.game = @"Spin the Bottle";
	player.rating = 5;
	[players addObject:player];
	player = [[Player alloc] init];
	player.name = @"Dave Brubeck";
	player.game = @"Texas Hold’em Poker";
	player.rating = 2;
	[players addObject:player];
	UITabBarController *tabBarController =
     (UITabBarController *)self.window.rootViewController;
	UINavigationController *navigationController =
     [[tabBarController viewControllers] objectAtIndex:0];
	PlayersViewController *playersViewController =
     [[navigationController viewControllers] objectAtIndex:0];
	playersViewController.players = players;
    return YES;
}

questo semplicemente crea gli oggetti Player ma le ultime righe fanno qualcosa di speciale…andiamo ad esaminarle:

Vogliamo assegnare l’array dei players alle proprietà contenute in PlayersViewController per poi poterlo usare come sorgente dei dati, ma l’AppDelegate non sa nulla di PalyersViewController per ora quindi deve scavare nello storyboard per trovarlo. Questa è sicuramente una delle limitazioni diello storyboard che trovo molto noioso. Con interface builder hai sempre un riferimento all’AppDelegate grazie a MainWindows.xib.

Apriamo il file PlayersViewController.m e cambiamo il data source come segue:

#import "Player.h"
@synthesize players;
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
	return 1;
}

- (NSInteger)tableView:(UITableView *)tableView
  numberOfRowsInSection:(NSInteger)section
{
	return [self.players count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier =@"PlayerCell";
UITableViewCell *cell = [tableView
 dequeueReusableCellWithIdentifier:CellIdentifier];
<span id="__mceDel"> Player *player = [self.players objectAtIndex:indexPath.row];<i>
</i></span>
    if (cell == nil) {
        cell = [[UITableViewCell alloc]
          initWithStyle:UITableViewCellStyleDefault
          reuseIdentifier:CellIdentifier];
    }
 cell.textLabel.text = player.name;
 cell.detailTextLabel.text = player.game;
 return cell;
}

Semplice vero? Non abbiamo fatto altro che importare nella tableview l’array e stabilito il data source.

Eseguiamo l’applicazione e vediamo che ha i giocatori all’interno:

xcode storyboard

Per adesso abbiamo usato solo il tipo standard subtitle della tableview ma possiamo personalizzarlo aggiungendo una imageview per far visualizzare il punteggio con le stelle:

xcode storyboard

Modifichiamo il file PlayersViewController dove è presente il data source della TableView:

- (UITableViewCell *)tableView:(UITableView *)tableView
  cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
	UITableViewCell *cell = [tableView
      dequeueReusableCellWithIdentifier:@&quot;PlayerCell&quot;];
	Player *player = [self.players objectAtIndex:indexPath.row];
	UILabel *nameLabel = (UILabel *)[cell viewWithTag:100];
	nameLabel.text = player.name;
	UILabel *gameLabel = (UILabel *)[cell viewWithTag:101];
	gameLabel.text = player.name;
	UIImageView * ratingImageView = (UIImageView *)
      [cell viewWithTag:102];
	ratingImageView.image = [self imageForRating:player.rating];
    return cell;
}

- (UIImage *)imageForRating:(int)rating
{
	switch (rating)
	{
		case 1: return [UIImage imageNamed:@&quot;1StarSmall.png&quot;];
		case 2: return [UIImage imageNamed:@&quot;2StarsSmall.png&quot;];
		case 3: return [UIImage imageNamed:@&quot;3StarsSmall.png&quot;];
		case 4: return [UIImage imageNamed:@&quot;4StarsSmall.png&quot;];
		case 5: return [UIImage imageNamed:@&quot;5StarsSmall.png&quot;];
	}
	return nil;
}

Fino ad ottenere questo risultato:

xcode storyboard

Per commenti o problemi vi aspetto nel post ufficiale sul forum