際際滷

際際滷Share a Scribd company logo
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

  Unorigine dati

  Un delegato

Iniziamo dalla prima

Una UITableView 竪 una classe che presenta una tabella a video
Origine dati per una table view
Unorigine 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 linserimento, leliminazione e
lordinamento di righe nella tabella

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

Consente allapplicazione host di avere maggior controllo
sullaspetto e il comportamento della casella

Riceve notifiche per le varie azioni dellutente

Altri metodi consentono al delegato di fornire view
personalizzate
Navighiamo nella tabella
Creiamo 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 FsO

Per tornare alla precedente astrazione vediamo come:

  RootViewController fa da delegato e origine dati

  La classe fornisce i metodi minimi per eseguire lapplicazione
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 dellarray
- (NSInteger)tableView:(UITableView *)tableView
!   !      !    numberOfRowsInSection:(NSInteger)section {
!   return [moviesArray count];
}




    Il codice precedente mostra che la tabella ha una sola riga

    Al momento dellesecuzione 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:

  linizializzatore 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

Labbiamo 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" nellheader

Creiamone unistanza in IB

Trasciniamo UViewController dalla Lib in RootViewController

Come identity impostiamo MovieEditorViewController

Connettiamo movieEditor alloggetto view controller
Modifica di un elemento
    tableView: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 dellArrray

Questo contenuto 竪 poi passato al campo movie del VC

La propriet navigationController 竪 ereditata da RVC

Questa propriet cerca allinterno della gerarchia fino a trovare
un riferimento ad un UINavigationController
Il pulsante Done
Per lIBAction 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 lindice dellarra y corrispondente a editingMovie

        Creiamo un NSIndexPath a quella riga e ricarichiamo la riga
Aggiungiamo un elemento
    Prima 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 sulloggetto target

    Loggetto target in questo caso 竪 proprio il RVC
Implementiamo lazione


     -(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 Files 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 VC
      Invece 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 unistruzione @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 linterfaccia 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
Modifiche
Per connettere loutlet, apriamo RVC.xib e aggiungiamo un VC

Impostare la classe DVDCabinetController nella finestra
Identity

Connettere il Files Owner alloutlet cabinetController

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

Sostituiamo la UIView con una UITableView

Impostiamo come classe delloggetto Files Owner DVDCC
Connettiamo gli oggetti

Creiamo le connessioni necessarie

Trasciniamo dalloutlet view del FsO al nuovo oggetto
TableView

Scegliere loutlet view

Connettere dataSource e delegate al Files 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 sullApple Campus

Ora vogliamo che la mappa venga centrata nuovamente e
ingrandita
Centrare le mappe
Per 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 unanimazione

      Impostiamo anche un IBOutlet nel codice per MKView
Aggiungere annotazioni
Inseriamo   MKMapView *_mapView;    nel file .h,

Chiamiamo lIBOutlet 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 annotazioni
Per aggiungere unannotazione dobbiamo creare la nostra
specifica implementazione di questo protocollo

Il protocollo definisce una propriet e due metodi opzionali

La propriet 竪 la localizzazione dellannotazione

I metodi sono title e subtitle

Vengono presentati sullannotazione 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 dimplementazione

       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 unazione

    Teoricamente potremmo decidere di scatenare unazione alla
    pressione del tasto

    Definiamo lIBAction 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 Files Owner
Aggiungiamo la nota alla
           mappa
Nel metodo viewDidApper: possiamo inserire lanimazione

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 dinserimento con [self.mapView
addAnnotation:sel.newAnnotation];
ALLA PROSSIMA LEZIONE!

More Related Content

Programming iOS lezione 2

  • 2. 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
  • 3. Dettagli Una tabella 竪 costituita da tre elementi: Una view Unorigine dati Un delegato Iniziamo dalla prima Una UITableView 竪 una classe che presenta una tabella a video
  • 4. Origine dati per una table view Unorigine 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 linserimento, leliminazione e lordinamento di righe nella tabella I metodi richiesti sono tableView:numberOfRowsInSection: e TableView:cellForRowAtIndexPath:
  • 5. Delegato Consente allapplicazione host di avere maggior controllo sullaspetto e il comportamento della casella Riceve notifiche per le varie azioni dellutente Altri metodi consentono al delegato di fornire view personalizzate
  • 6. Navighiamo nella tabella Creiamo 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
  • 7. Dove si trova la tabella? Aprite RootViewController.xib, e vedrete il contenuto come singolo oggetto UITableView Vediamo come dataSource e delegate sono connessi al FsO Per tornare alla precedente astrazione vediamo come: RootViewController fa da delegato e origine dati La classe fornisce i metodi minimi per eseguire lapplicazione
  • 8. 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
  • 9. 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..
  • 10. 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];
  • 11. Aggiornamento Aggiorniamo i nostri metodi per restituire la lunghezza dellarray - (NSInteger)tableView:(UITableView *)tableView ! ! ! numberOfRowsInSection:(NSInteger)section { ! return [moviesArray count]; } Il codice precedente mostra che la tabella ha una sola riga Al momento dellesecuzione verr richiamato il metodo TableView:cellForRowAtIndexPath: Si otterr quindi una UITableView per tale riga Per personalizzare la cella inserire il codice dopo il commento
  • 12. 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}
  • 13. 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
  • 14. Propriet della cella La cella di default ha tre propriet principali textLabel detailTextLabel imageView Per usare detailTextLabel dobbiamo usare uno stile differente di cella
  • 15. 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)
  • 16. Riutilizzo delle celle Nel metodo che stiamo utilizzando cellForRowAtIndexPath: esiste: linizializzatore 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
  • 17. 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:
  • 18. 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:
  • 19. 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:
  • 20. Navigar m竪 dolce.. Nel capitolo precedente abbiamo usato una view modale Labbiamo 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
  • 21. 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;
  • 22. Dettagli Facciamo @synthesize per questa propriet nel file .m Inseriamo #import "MovieEditorViewController.h" nellheader Creiamone unistanza in IB Trasciniamo UViewController dalla Lib in RootViewController Come identity impostiamo MovieEditorViewController Connettiamo movieEditor alloggetto view controller
  • 23. Modifica di un elemento tableView: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]; }
  • 24. Due commenti veloci Abbiamo istanziato una variabile di tipo Movie Questa memorizza il contenuto dellArrray Questo contenuto 竪 poi passato al campo movie del VC La propriet navigationController 竪 ereditata da RVC Questa propriet cerca allinterno della gerarchia fino a trovare un riferimento ad un UINavigationController
  • 25. Il pulsante Done Per lIBAction 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
  • 26. 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 lindice dellarra y corrispondente a editingMovie Creiamo un NSIndexPath a quella riga e ricarichiamo la riga
  • 27. Aggiungiamo un elemento Prima 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 sulloggetto target Loggetto target in questo caso 竪 proprio il RVC
  • 28. Implementiamo lazione -(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]; }
  • 30. 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
  • 31. 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 Files Owner
  • 32. 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
  • 33. Comportamento del VC Invece 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; }
  • 34. 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
  • 35. 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 unistruzione @synthesize Modifichiamo viewDidLoad: per visualizzare il testo nel pulsante
  • 36. 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
  • 37. Creiamo il nuovo VC Creiamo una sottoclasse di UITableViewController Creiamo il nuovo file NIB che contiene linterfaccia 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
  • 38. 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
  • 39. Modifiche Per connettere loutlet, apriamo RVC.xib e aggiungiamo un VC Impostare la classe DVDCabinetController nella finestra Identity Connettere il Files Owner alloutlet cabinetController Creiamo una nuova View da Add->New File, View XIB Sostituiamo la UIView con una UITableView Impostiamo come classe delloggetto Files Owner DVDCC
  • 40. Connettiamo gli oggetti Creiamo le connessioni necessarie Trasciniamo dalloutlet view del FsO al nuovo oggetto TableView Scegliere loutlet view Connettere dataSource e delegate al Files Owner Per visualizzare i dati 竪 necessario implementare i soliti 2 metodi
  • 41. 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]; ! }
  • 42. 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
  • 43. 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; }
  • 44. 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
  • 46. 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
  • 47. 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
  • 48. 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 sullApple Campus Ora vogliamo che la mappa venga centrata nuovamente e ingrandita
  • 49. Centrare le mappe Per 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
  • 50. 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 unanimazione Impostiamo anche un IBOutlet nel codice per MKView
  • 51. Aggiungere annotazioni Inseriamo MKMapView *_mapView; nel file .h, Chiamiamo lIBOutlet 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
  • 52. Le annotazioni Per aggiungere unannotazione dobbiamo creare la nostra specifica implementazione di questo protocollo Il protocollo definisce una propriet e due metodi opzionali La propriet 竪 la localizzazione dellannotazione I metodi sono title e subtitle Vengono presentati sullannotazione entrambi Uno il titolo, uno il sottotitolo
  • 53. 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
  • 54. Il file dimplementazione 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
  • 55. Aggiungere unazione Teoricamente potremmo decidere di scatenare unazione alla pressione del tasto Definiamo lIBAction 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 Files Owner
  • 56. Aggiungiamo la nota alla mappa Nel metodo viewDidApper: possiamo inserire lanimazione 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 dinserimento con [self.mapView addAnnotation:sel.newAnnotation];