Eccomi al mio primo tutorial su DevAPP in cui vedremo i principali elementi che ci mette a disposizione il MapKit. In questa prima parte vedremo come zoommare sulla posizione dell’utente quando vengono acquisite le coordinate GPS e come aggiungere delle annotation view sulla nostra mappa. Premetto che io non ho l’abitudine di usare Interface Builder e che tutte le view che andremo ad utilizzare verranno create pragmaticamente.

Setup

Cominciamo col creare un nuovo progetto Window-Based in modo che vengano automaticamente generati solamente la window e l’application delegate. Una volta fatto ciò la prima cosa che dobbiamo fare è importare il framework MapKit: facciamo click secondario sul gruppo framework in Groups & Files, selezionando Add – Existing framework e scegliamo MapKit.framework. Importiamolo ora nel file nomeprogetto_prefix.pch che trovate nel gruppo Other Sources in modo che tutte le classi che andremo a creare potranno utilizzarlo. Il prefix header alla fine dovrà essere così:

1
2
3
4
5
#ifdef __OBJC__
    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    #import <MapKit/MapKit.h>
#endif

Creazione della map view

A questo punto ci ritroviamo con un’applicazione pronta ad utilizzare le mappe. Creiamo ora un nuovo UIViewController che chiameremo MapViewController. Cominciamo con l’aprire il file MapViewController.h e modifichiamolo in modo da adottare il protocollo MKMapViewDelegate.

1
2
3
@interface MapViewController : UIViewController <MKMapViewDelegate>
 
@end

Apriamo invece ora il file MapViewController.m e ridefiniamo la funzione loadView per poter creare la nostra map view.

1
2
3
4
5
6
7
- (void)loadView {
    self.title = @"Mappa";
    MKMapView *map= [[[MKMapView alloc] init] autorelease];
    map.delegate = self;
    map.showsUserLocation = YES;
    self.view = map;
}

In questo metodo allochiamo una nuova map view con abilitata la visualizzazione della posizione dell’utente, gli settiamo il controller stesso come delegate e visualizziamo la mappa assegnandola alla view del controller.

Se ora compiliamo ed eseguiamo l’applicazione l’unica cosa che vedremo sarà una schermata bianca (il colore di default della window). Questo perché non abbiamo ancora aggiunto questa vista nella finestra. Per far ciò apriamo il file di implementazione dell’application delegate e modifichiamo la funzione applicationDidFinishLaunching come segue:

1
2
3
4
5
6
- (void)applicationDidFinishLaunching:(UIApplication *)application {
    MapViewController *rootViewController = [[[MapViewController alloc] init] autorelease];
    UINavigationController navController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
    [window addSubview:navController.view];
    [window makeKeyAndVisible];
}

Quello che facciamo è semplicemente creare un UINavigationController (il perché lo capiremo quando parleremo delle annotation) usando il nostro MapViewController come root controller e visualizzare la sua view nella finestra.

Se ora compiliamo vedremo la mappa e, dopo un breve lasso di tempo, il puntino blu della nostra posizione a Cupertino (se siamo sul simulatore).

Immagine 1

Zoommiamo sulla posizione dell’utente

Per far questo dobbiamo ridefinire un metodo del protocollo MKMapViewDelegate. Riapriamo quindi il file MapViewController e aggiungiamo il seguente metodo:

1
2
3
4
5
6
7
8
9
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
    for (MKAnnotationView *annotationView in views) {
        if (annotationView.annotation == mapView.userLocation) {
            MKCoordinateSpan span = MKCoordinateSpanMake(0.3, 0.3);
            MKCoordinateRegion region = MKCoordinateRegionMake(mapView.userLocation.coordinate, span);
            [mapView setRegion:region animated:YES];
        }
    }
}

Questo metodo viene chiamato ogniqualvolta viene aggiunta una annotation alla mappa e quindi anche quando viene aggiunto il pallino blu relativo alla posizione dell’utente. Il codice scritto non fa altro che cercare tra le annotation view aggiunte se è presente quella relativa alla posizione dell’utente (mapView.userLocation). La regione che deve visualizzare la mappa viene settata in modo che in modo che sia centrata sulle coordinate dell’utente e in modo che la distanza nord-sud o ovest-est (dipende dal fatto che la mappa sia in landscape o portrait) sia di 0.3 gradi (stando alla documentazione 1 grado è circa 111km). Con la funzione setRegion:animated modifichiamo l’area visualizzata dalla mappa tramite un’animazione.

Se ora compiliamo ed eseguiamo l’applicativo vediamo che, quando viene acquisita la posizione dell’utente, la view viene modificata tramite un’animazione.

Immagine 2

Aggiunta di una annotation view personalizzata

Innanzitutto dobbiamo avere una annotation (ovvero un oggetto conforme al protocollo MKAnnotation) da visualizzare. Creiamo perciò una nuova classe (io l’ho chiamata GoogleHQAnnotation) che estende NSObject a cui facciamo adottare il protocollo MKAnnotation. Per esempio io ho creato l’annotazione relativa alla sede di Google a Mountain View in California. Modifichiamo il file .h in modo di avere il seguente codice:

1
2
3
4
5
@interface GoogleHQAnnotation : NSObject <MKAnnotation>  {
	CLLocationCoordinate2D coordinate;
}
 
@end

L’attributo coordinate è un attributo richiesto dal protocollo MKAnnotation e siamo quindi obbligati a dichiararlo. Passiamo ora al file di implementazione:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#import "GoogleHQAnnotation.h"
 
@implementation GoogleHQAnnotation
 
- (id)init {
    coordinate.longitude = -122.084095;
    coordinate.latitude = 37.422006;
    return [super init];
}
 
@synthesize coordinate;
 
- (NSString *)title {
    return @"Google HQ";
}
 
- (NSString *)subtitle {
    return @"(650) 253-0000‎";
}
 
@end

Si tratta di un semplice file di implementazione dove nella ridefinizione del metodo init ci limitiamo a settare le coordinate che puntano la sede di Google e che implementa i metodi richiesti dal protocollo (ovvero titlesubtitlecoordinate). L’unica cosa strana è che abbiamo utilizzato la direttiva @synthesize senza aver dichiarato la property nel file di interfaccia. Questo non è un errore in quanto, adottando il protocollo MkAnnotation ereditiamo pure la proprietà dichiarata come @property (nonatomic, readonly) CLLocationCoordinate2D coordinate in esso definita.

Torniamo al MapViewController. Dobbiamo aggiungere l’annotazione alla map view. Possiamo quindi aggiungerla, per esempio, in fase di creazione nella loadView andando ad aggiungere la seguente riga di codice alla fine della funzione:

1
[map addAnnotation:[[[GoogleHQAnnotation alloc] init] autorelease]];

ricordandoci di importare il file .h della classe GoogleHQAnnotation.

A questo punto se eseguiamo l’applicativo già verrà creata la annotation view di default, ma noi ne vogliamo una personalizzata. Dobbiamo quindi ridefinire il metodo mapView:viewForAnnotation: ereditato dal protocollo MKMapViewDelegate. Questo metodo è quello a cui è delegata la creazione delle annotation view di ogni annotation. Lo implementeremo nel seguente modo:

1
2
3
4
5
6
7
8
9
10
11
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation {
    if (annotation == mapView.userLocation) {
        return nil;
    }
    MKPinAnnotationView *pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"Pin"] autorelease];
    pinView.pinColor = MKPinAnnotationColorPurple;
    pinView.canShowCallout = YES;
    pinView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    pinView.animatesDrop = YES;
    return pinView;
}

Analizziamo ora il codice scritto. Nel blocco if controlliamo se l’annotazione di cui dobbiamo creare la view è quella relativa alla posizione dell’utente. In tal caso diamo come valore di ritorno nil che serve a far capire al sistema che per quella annotazione deve usare la annotation view di default. In caso contrario creiamo un pin da visualizzare avente le seguenti caratteristiche:

  • Colore viola (pinView.pinColor = MKPinAnnotationColorPurple)
  • Permette di visualizzare il callaut, ovvero il fumetto che appare quando tappate il pin (pinView.canShowCallout = YES)
  • Ha uno disclosure button sulla parte destra del callout (pinView.rightCalloutAccessoryView = …)
  • Appare tramite un’animazione (pinView.animatesDrop = YES)

La funzione termina con una return pinView che serve a far capire al sistema che quella annotazione verrà rappresentata sulla mappa dalla annotation view che abbiamo creato noi e non da quella di default.

Se ora compiliamo ed eseguiamo l’applicazione vedremo il nostro bel pin viola e se lo tappiamo apparirà il callout con titolo e sottotitolo. Ma c’è ancora qualcosa che manca. Infatti possiamo premere il disclosure button tutte le volte che vogliamo ma non accadrà niente. Per gestire questo evento dobbiamo ridefinire un metodo ereditato dal protocollo MKMapViewDelegate che viene invocato ogni volta che viene tappato un button presente in un qualsiasi callout. Aggiungiamo quindi il seguente codice al MapViewContoller:

1
2
3
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
	[self.navigationController pushViewController:[[[UIViewController alloc] init] autorelease] animated:YES];
}

In pratica così facendo, quando premeremo il disclosure button verremo reindirizzati ad una nuova vista che sarà la base di partenza per la seconda parte tutorial.

Immagine 3

Se avete bisogno del codice lo potete tranquillamente scaricare comprensivo di commenti da google code.