Programming iOS lezione 2
-
Upload
nodelay-software -
Category
Technology
-
view
2.630 -
download
0
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!