Programming iOS lezione 2

Post on 03-Dec-2014

2.625 views 0 download

Tags:

description

 

Transcript of Programming iOS lezione 2

LE TABLE VIEW

Cosa sono le Table View?

Le table view sono gli elementi più comuni

Quasi tutte le applicazioni di base di iPhone le usano

Rendono facile la visualizzazione delle informazioni

Possono essere di due tipi differenti:

Plain

Grouped

Dettagli

Una tabella è costituita da tre elementi:

Una view

Un’origine dati

Un delegato

Iniziamo dalla prima

Una UITableView è una classe che presenta una tabella a video

Origine dati per una table view

Un’origine dati è un oggetto che descrive la relazione tra UITableView e il suo contenuto

Gran parte delle funzioni sono svolte dal protocollo UITableViewDataSource

Dispone di metodi per gestire l’inserimento, l’eliminazione e l’ordinamento di righe nella tabella

I metodi richiesti sono tableView:numberOfRowsInSection: e TableView:cellForRowAtIndexPath:

Delegato

Consente all’applicazione host di avere maggior controllo sull’aspetto e il comportamento della casella

Riceve notifiche per le varie azioni dell’utente

Altri metodi consentono al delegato di fornire view personalizzate

Navighiamo nella tabellaCreiamo un nuovo progetto con Navigation Based Application

Apriamo File->new Project in Xcode

Non selezionare Use Core Data for Storage

Assegnate al progetto MovieTable come nome

Ora apriamo MainWindow.xib in IB e passiamo alla view ad elenco

Il progetto ha un navigation controller con una navigation bar

Dove si trova la tabella?

Aprite RootViewController.xib, e vedrete il contenuto come singolo oggetto UITableView

Vediamo come dataSource e delegate sono connessi al F’sO

Per tornare alla precedente astrazione vediamo come:

RootViewController fa da delegato e origine dati

La classe fornisce i metodi minimi per eseguire l’applicazione

Nota mnemonica

Tenete sempre presente una cosa quando avete a che fare con Tables

È SEMPRE necessario implementare le seguenti interfacce:

UITableViewDataSource

UITableViewDelgate

Rigorosamente da aggiungere all’@interface

Sviluppiamo il modello MVC

Per il modello prendiamo la classe Movie della lezione prima

In group and files fare Ctrl+Click sulla classe e scegliere Add->Existing Files

Navigare fino al precedente progetto Movie e importare i due files .h e .m

Assicurarsi di selezionare Copy Items into destination..

Modifiche al codice

Aggiungete #import<Movie.h> al RootViewController.h

Dichiarate la variabile istanza NSMutableArray moviesArray;

Decommentiamo il metodo viewDidLoad in RootViewController.m e aggiungere il codice

- (void)viewDidLoad { [super viewDidLoad]; moviesArray = [[NSMutableArray alloc] init]; Movie *aMovie = [[Movie alloc] init];! aMovie.title = @"Plaything Anecdote";! aMovie.boxOfficeGross = [NSNumber numberWithInt: 191796233];! aMovie.summary =! @"Did you ever think your dolls were really alive? Well, they are.";! [moviesArray addObject: aMovie];! [aMovie release];

Aggiornamento Aggiorniamo i nostri metodi per restituire la lunghezza dell’array

- (NSInteger)tableView:(UITableView *)tableView! ! ! numberOfRowsInSection:(NSInteger)section {! return [moviesArray count];}

Il codice precedente mostra che la tabella ha una sola riga

Al momento dell’esecuzione verrà richiamato il metodo TableView:cellForRowAtIndexPath:

Si otterrà quindi una UITableView per tale riga

Per personalizzare la cella inserire il codice dopo il commento

NSIndexPath

Perché usiamo NSIndexPath?

È un oggetto che specifica un percorso attraverso una struttura ad albero

Parte da un insieme di numeri interi che iniziano da zero

Su iPhone OS questa classe è estesa con proprietà specifiche

La sezione e la riga son indicate come indexPath.{section,row}

Aggiunta del codice

Quindi nel metodo di TableView:cellForRowAtIndexPath: aggiungeremo:

Movie *aMovie = [moviesArray objectAtIndex:indexPath.row];cell.textLabel.text = aMovie.title;cell.detailTextLabel.text = aMovie.summary;

La prima riga contiene il membro di moviesArray

Nella seconda e terza presentiamo titolo e dettaglio del film

Proprietà della cella

La cella di default ha tre proprietà principali

textLabel

detailTextLabel

imageView

Per usare detailTextLabel dobbiamo usare uno stile differente di cella

Stili di Cella

Esistono quattro stili di cella differenti

UITableViewCellStyleDefault (Testo con allineamento a Sx)

UITableViewCellStyleSubtitle (Seconda riga con dettagli)

UITableViewCellStyleValue1 (Dettagli a destra)

UITableViewCellStyleValue2 (testo blu a Dx e dettagli a dx)

Riutilizzo delle celle

Nel metodo che stiamo utilizzando cellForRowAtIndexPath: esiste:

l’inizializzatore per UITableViewCell: richiede la stringa:

CellIdentifier: serve per recuperare la cella nel caso di scomparsa dallo schermo

È una cache per il contenuto che non viene ricaricato

Si reimposta il contenuto invece di creare nuove celle

Aggiungere la rimozione

È semplice e veloce scegliere dara la possibilità di rimuovere celle

Basta decommentare la funzione tableView:canEditRowAtIndexPath:

Basta cambiare il valore di ritorno su YES

Per la rimozione vera e propria invece è necessario utilizzare

tableView:commitEditingStyle:forRowAtIndexPath:

Scriviamo il codice

Come al solito è tutto già implementato

Dobbiamo solo supportare un pezzo di codice

In questo caso UITableViewCellEditingStyleDelete

UI TableView fornisce già il metodo deleteRowsAtIndexPaths:withRowsAnimation:

Per il modello c’è removeObjectAtIndex:

Codice, codice, codice...- (void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { // Delete the row from the data source. [moviesArray removeObjectAtIndex: indexPath.row]; [tableView deleteRowsAtIndexPaths: [NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];}}

In questo modo otteniamo il classico comportamento trascina per eliminare

Ora impostiamo anche il bottone di edit Decommentiamo self.navigationItem.leftBarButtonItem = self.editButtonItem; in ViewDidLoad:

Navigar m’è dolce..

Nel capitolo precedente abbiamo usato una view modale

L’abbiamo presentata col metodo presentModalViewController:

Ora la navigazione è gestita da UINavigationController

Dobbiamo prendere la classe MovieEditorViewController

Copiamo le due classi e il file xib con la solita tecnica

Spostiamo lo xib nel gruppo Resources

Alcune Modifiche

Visto che precedentemente occupava tutto lo schermo

Ora la view va modificata con Simulated Interface Elements in IB

Impostare Top Bar a Navigation Bar

Creiamo un IBOutlet in RootViewController

Aggiungiamo MovieEditorViewController *movieEditor; ..

.. la property associata IBOutlet MEVC *movieEditor;

Dettagli

Facciamo @synthesize per questa proprietà nel file .m

Inseriamo #import "MovieEditorViewController.h" nell’header

Creiamone un’istanza in IB

Trasciniamo UViewController dalla Lib in RootViewController

Come identity impostiamo MovieEditorViewController

Connettiamo movieEditor all’oggetto view controller

Modifica di un elementotableView:DidSelectRowAtIndexPath: fornisce un template

Questo crea una nuova view

A noi non serve, useremo quella già fatta

Modificheremo la classe nel seguente modo:- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

// Navigation logic may go here -- for example, create and push another view controller.! // AnotherViewController *anotherViewController = [[AnotherViewController alloc] initWithNibName:@"AnotherView" bundle:nil];! // [self.navigationController pushViewController:anotherViewController animated:YES];! // [anotherViewController release];

! editingMovie = [moviesArray objectAtIndex:indexPath.row];! movieEditor.movie = editingMovie;! [self.navigationController pushViewController:movieEditor animated:YES];}

Due commenti veloci

Abbiamo istanziato una variabile di tipo Movie

Questa memorizza il contenuto dell’Arrray

Questo contenuto è poi passato al campo movie del VC

La proprietà navigationController è ereditata da RVC

Questa proprietà cerca all’interno della gerarchia fino a trovare un riferimento ad un UINavigationController

Il pulsante Done

Per l’IBAction done: bisogna cambiare il comportamento

In questo caso sarà necessario chiamare il popViewController

Con la proprietà animated: settata a YES;

Anche MEVC può accedere alla proprietà ereditata dal navigationController

Ora il RVC ottiene il callback a viewWillAppear: e aggiorniamo la view

viewWillAppear:- (void)viewWillAppear:(BOOL)animated {

[super viewWillAppear:animated];! NSLog (@"viewWillAppear");! // update table view if a movie was edited! if (editingMovie) {! ! NSIndexPath *updatedPath = [NSIndexPath! ! ! indexPathForRow: [moviesArray indexOfObject: editingMovie] inSection: 0];! ! NSArray *updatedPaths = [NSArray arrayWithObject:updatedPath];! ! [self.tableView reloadRowsAtIndexPaths:updatedPaths withRowAnimation:NO];! ! editingMovie = nil;! }}

Identifichiamo la fase di modifica

Identifichiamo la riga di tabella aggiornata

Otteniamo l’indice dell’arra y corrispondente a editingMovie

Creiamo un NSIndexPath a quella riga e ricarichiamo la riga

Aggiungiamo un elementoPrima abbiamo usato leftBarButtonItem

Ora per il pulsante Aggiungi definiamo una IBAction in RVC.h-(IBAction) handleAddTapped;

In IB trascinare su RVC un UIBarButtonItem

La connessione avviene in questo caso diversamente

Il metodo selector viene richiamato sull’oggetto target

L’oggetto target in questo caso è proprio il RVC

Implementiamo l’azione

-(IBAction) handleAddTapped {! NSLog (@"handleAddTapped");! Movie *newMovie = [[Movie alloc] init];! [moviesArray addObject: newMovie];! editingMovie = newMovie;! movieEditor.movie = editingMovie;! [self.navigationController pushViewController:movieEditor animated:YES];! // update UITableView (in background) with new member! NSIndexPath *newMoviePath = [NSIndexPath indexPathForRow: [moviesArray count]-1 inSection:0];! NSArray *newMoviePaths = [NSArray arrayWithObject:newMoviePath];! [self.tableView insertRowsAtIndexPaths:newMoviePaths withRowAnimation:NO];}

LA NAVIGAZIONE

Come funziona la navigazione

C’è un Navigation Controller

La navigazione è organizzata con uno stack

Tipicamente si parte da RootViewController

Si passa ad una serie più o meno infinita di altri VC

Ognuno riporta un titolo ed un link al precedente

Si passa dal generale al particolare

Esaminiamo le cose

Apriamo un nuovo progetto chiamato DVDCase

Andiamo a vedere MainWindow.xib

Esaminiamo il Navigation controller

Troviamo finalmente la Table View

Notiamo che sia dataSource che delegate sono del File’s Owner

Piccole modifiche

Sono implementati i metodi numberOfRowInSection:

e cellForRowAtIndexPath: li modifichiamo leggermente:- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // return 0;! return 2;}

In questo caso abbiamo solo due sezioni

Comportamento del VCInvece modifichiamo alcune righe

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) {! cell = [[[UITableViewCell alloc] initWithStyle:UITableViewStylePlain reuseIdentifier:CellIdentifier] ! ! autorelease]; } cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; switch (indexPath.row) { case 0: cell.textLabel.text = @"Home"; break; case 1: cell.textLabel.text = @"Work"; break; default: break; } return cell;}

Altre modifiche

Andiamo a implementare didSelectRowAtIndexPath:- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if(0 == indexPath.row) { self.cabinetController.key = @"home"; self.cabinetController.title = @"Home"; } else { self.cabinetController.key = @"work"; self.cabinetController.title = @"Work"; } [self.navigationController pushViewController:self.cabinetController animated:YES];}

Impostiamo il titolo in maniera condizionale

Creeremo anche il VC CabinetController

Precisazioni

Abbiamo impostato il titolo del VC in maniera condizionale

La chiamata a pushViewController rende attiva la view del VC

La proprietà cabinetController va dichiarata al RVC

Aggiungiamo una variabile istanza e un’istruzione @synthesize

Modifichiamo viewDidLoad: per visualizzare il testo nel pulsante

Personalizzazione- (void)viewDidLoad {

[super viewDidLoad];! self.title = @"Cases";}

Ora creiamo il VC cabinetController

Vedremo che è possibile personalizzare qualcosa in più

Possiamo aggiungere un bottone a sx

Istanza personalizzata di UIBarButtonItem

Con customView si potrebbe sostituire del tutto la view

Creiamo il nuovo VC

Creiamo una sottoclasse di UITableViewController

Creiamo il nuovo file NIB che contiene l’interfaccia utente

Configurare il file NIB per avere una TV e al nostro VC

Aggiungere un outlet in RVC per farle conoscere il nuovo VC

Aggiornare RVC.xib per impostare questo outlet

Aggiungiamo quello che serve

Aggiungiamo un file sottoclasse di UITableViewController

Chiamiamola DVDCabinetController

Aggiungiamo il nuovo outlet a RVC, aggiungiamo import e synth@class DVDCabinetController;

@interface RootViewController : UITableViewController {! DVDCabinetController *cabinetController;}

@property(nonatomic, retain) IBOutlet DVDCabinetController *cabinetController;

@end

ModifichePer connettere l’outlet, apriamo RVC.xib e aggiungiamo un VC

Impostare la classe DVDCabinetController nella finestra Identity

Connettere il File’s Owner all’outlet cabinetController

Creiamo una nuova View da Add->New File, View XIB

Sostituiamo la UIView con una UITableView

Impostiamo come classe dell’oggetto File’s Owner DVDCC

Connettiamo gli oggetti

Creiamo le connessioni necessarie

Trasciniamo dall’outlet view del F’sO al nuovo oggetto TableView

Scegliere l’outlet view

Connettere dataSource e delegate al File’s Owner

Per visualizzare i dati è necessario implementare i soliti 2 metodi

Implementiamo

Nel file DVDCabinetController.h@interface DVDCabinetController : UITableViewCell {! NSDictionary *data;! NSString *key;

}

@property (nonatomic, retain) NSString *key;

Nel file DVDCabinetController.m-(void)viewDidLoad {! [super viewDidLoad];! NSArray *keys = [NSArray arrayWithObjects:@"Home",@"Work", nil];! NSArray *homeDVDs = [NSArray arrayWithObjects:@"Thomas the Builder", nil];! NSArray *workDVDs = [NSArray arrayWithObjects:@"Intro to Blender",nil];! NSArray *values = [NSArray arrayWithObjects:homeDVDs, workDVDs, nil];! data = [[[NSDictionary alloc] initWithObjects: values forKeys: keys];!}

Ultimi ritocchi

Abbiamo fatto due dizionari per contenere i valori memorizzati in RVC

Tipicamente non si usa questo tipo di approccio

Risulta però così più leggibile il codice

Modifichiamo i metodi per il ritorno del numero di valori

E personalizziamo la cella affinché mostri il valore corretto

Ancora modifiche

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // Return the number of sections. return 1;}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Return the number of rows in the section. return [[data valueForKey:self.key]count];}

// Customize the appearance of table view cells.- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; } // Configure the cell...! cell.textLabel.text = [[data valueForKey:self.key] objectAtIndex:indexPath.row];! return cell;}

Ultima porzione di codice

Finiamo due modifiche per viewWillAppear:- (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated];! [self.tableView reloadData];}

E tableView:didSelectRowAtIndexPath:- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // Navigation logic may go here. Create and push another view controller.! [self.navigationController popViewControllerAnimated:YES];}

Finalmente torniamo indietro nella navigazione tramite il riferimento al NavigationController

MAPKIT

Cosa fa MapKit

È la classe che si occupa della generazione di mappe

È particolarmente utile in virtù della localizzazione del device

Unendo le due funzionalità si possono ottenere ottimi risultati

La nostra piccola applicazione centrerà la mappa sulla nostra posizione corrente

Primo passo

Come prima cosa è necessario aggiungere al progetto i Framework

Creiamo un nuovo progetto e chiamiamolo FindMe

Utilizziamo il template View-Based Application

Aggiungiamo i Framework MapKit e CoreLocation

Apriamo il file NIB e trascinate un MapView

Quasi finito

Abbiamo quasi finito, basta selezionare Show User Location

Selezioniamo il tipo di mappa (Map)

Salviamo e eseguiamo

Il simulatore mostrerà una posizione fittizia sull’Apple Campus

Ora vogliamo che la mappa venga centrata nuovamente e ingrandita

Centrare le mappePer centrare le mappe bisogna usare il metodo setRegion:

Il parametro region: è una struttura C simile a CGRect

MKCoordinateRegion ha due parti center e span.center

Il primo è un CLocationCoordinate2D con coordinate del punto

Il secondo è un MKCoordinateSpan e specifica la variazione in gradi delle coordinate della regione da includere

Centriamo la mappa con .15 di differenza tra le due misure

Codice per centrare le mappe- (void)setCurrentLocation:(CLLocation *)location {

MKCoordinateRegion region = {{0.0f, 0.0f}, {0.0f, 0.0f}}; region.center = location.coordinate; region.span.longitudeDelta = 0.15f; region.span.latitudeDelta = 0.15f; [self.mapView setRegion:region animated:YES];}

Il Codice è abbastanza autoesplicativo, settiamo la regione

Prendiamo le coordinate della nostra posizione

Impostiamo anche lo span

Diciamo alla mappa di settarsi con un’animazione

Impostiamo anche un IBOutlet nel codice per MKView

Aggiungere annotazioniInseriamo MKMapView *_mapView; nel file .h,

Chiamiamo l’IBOutlet nella stessa maniera

E poi sintetizziamo dicendo che _mapView = mapView;

Teoricamente dovremo vedere anche il delegato del localizzatore

In realtà Show User Location farà il lavoro per noi

Per le annotazioni MKAnnotation non definisce le implementazioni pubbliche

Le annotazioniPer aggiungere un’annotazione dobbiamo creare la nostra specifica implementazione di questo protocollo

Il protocollo definisce una proprietà e due metodi opzionali

La proprietà è la localizzazione dell’annotazione

I metodi sono title e subtitle

Vengono presentati sull’annotazione entrambi

Uno il titolo, uno il sottotitolo

Creiamo un modello

Creiamo una classe che implementa <MKAnnotation>#import <Foundation/Foundation.h>#import <CoreLocation/CoreLocation.h>#import <MapKit/MapKit.h>

@interface ContactAnnotation : NSObject <MKAnnotation> { CLLocationCoordinate2D _coordinate; NSString *_title; NSString *_subtitle;}

+ (id)annotationWithCoordinate:(CLLocationCoordinate2D)coordinate;- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate;

@property (nonatomic, assign) CLLocationCoordinate2D coordinate;@property (nonatomic, copy) NSString *title;@property (nonatomic, copy) NSString *subtitle;

@end

Abbiamo definito il nostro protocollo, ora tocca alle classi

Il file d’implementazione

Andiamo a implementare il protocollo nel nostro file:#import "ContactAnnotation.h"@implementation ContactAnnotation

@synthesize coordinate = _coordinate;@synthesize title = _title;@synthesize subtitle = _subtitle;

+ (id)annotationWithCoordinate:(CLLocationCoordinate2D)coordinate { return [[[[self class] alloc] initWithCoordinate:coordinate] autorelease];}

- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate { self = [super init]; if(nil != self) { self.coordinate = coordinate; } return self;}

@end

Aggiungere un’azione

Teoricamente potremmo decidere di scatenare un’azione alla pressione del tasto

Definiamo l’IBAction choose: come segue- (IBAction)choose { UINavigationController *detailView = [[UINavigationController alloc] init]; detailView.viewDelegate = self; [self presentModalViewController:detailView animated:YES]; [detailView release];}

Aggiungiamo la definizione nel file header e creiamo la connessione al File’s Owner

Aggiungiamo la nota alla mappa

Nel metodo viewDidApper: possiamo inserire l’animazione

Possiamo inserire la nostra istanza di ContactAnnotation

Istanziamo la classe da qualche parte nel codice dove più ci serve

self.newAnnotation= [ContactAnnotation annotationWithCoordinate:coordinate];

Chiamiamo il metodo d’inserimento con [self.mapView addAnnotation:sel.newAnnotation];

ALLA PROSSIMA LEZIONE!