Olá povo,
Esse post é grande, mas é bem legal. Nele, vou falar sobre a utilização de Mapas no iOS. Conheceremos as principais classes do MapKit, e além desse objetivo principal, utlizaremos um conceito interessante do Objective-C, os blocks.
Para começar crie um novo projeto no Xcode (pode ser Single View Application sem utilizar Storyboards). Adicione os frameworks MapKit e CoreLocation à sua aplicação. Em seguida, crie uma nova classe chamada Endereço e deixe-a conforme abaixo:
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
@interface Endereco : NSObject<MKAnnotation>
@property (strong, nonatomic) NSString *titulo;
@property (strong, nonatomic) NSString *subtitulo;
- (id)initWithCoordinate:(CLLocationCoordinate2D) c;
@end
#import "Endereco.h"
@implementation Endereco
@synthesize titulo, subtitulo;
@synthesize coordinate;
- (id)initWithCoordinate:(CLLocationCoordinate2D) c {
coordinate = c;
NSLog(@"%f,%f", c.latitude, c.longitude);
return self;
}
- (NSString *)subtitle {
return subtitulo;
}
- (NSString *) title {
return titulo;
}
@end
A classe acima está implementando o protocolo MKAnnotation que exige que exista uma propriedade coordinate e os métodos subtitle e title. Isso porque, usaremos essa classe para representar um ponto no Mapa.
Agora deixe o ViewController da sua aplicação conforme abaixo (o nome da classe obviamente pode ser alterado...).
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import "Endereco.h"
@interface NGViewController : UIViewController
<UISearchBarDelegate, MKMapViewDelegate>
@property (weak, nonatomic) IBOutlet
UISearchBar *searchBar;
@property (weak, nonatomic) IBOutlet
MKMapView *mapa;
@property (weak, nonatomic) IBOutlet
UISegmentedControl *segMapType;
@property (strong, nonatomic)
Endereco *endereco;
- (IBAction)mapViewChanged:(id)sender;
@end
#import "NGViewController.h"
#import "Endereco.h"
@implementation NGViewController
@synthesize searchBar;
@synthesize mapa;
@synthesize segMapType;
@synthesize endereco;
#define API_KEY @"SUA_CHAVE"
#define TEMPLATE_URL
@"http://maps.google.com/maps/geo?q=%@&output=csv&key=%@"
#pragma mark - View lifecycle
- (void)viewDidUnload {
[self setSearchBar:nil];
[self setMapa:nil];
[self setSegMapType:nil];
[super viewDidUnload];
}
#pragma mark - SegmentedControl value changed
- (IBAction)mapViewChanged:(id)sender {
// De acordo com a posição selecionada no
// SegmentedControl, mudamos o tipo de
// visualização do mapa
switch ([segMapType selectedSegmentIndex]) {
case 0: mapa.mapType = MKMapTypeStandard; break;
case 1: mapa.mapType = MKMapTypeSatellite; break;
case 2: mapa.mapType = MKMapTypeHybrid; break;
}
}
#pragma mark - SearchBar methods
-(void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
// Ao clicar em cancelar na barra de busca,
// ocultamos o teclado
[self.searchBar resignFirstResponder];
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[self.searchBar resignFirstResponder];
[self retrieveLocation:self.searchBar.text];
}
#pragma mark - Utility Methods
- (void) retrieveLocation:(NSString *)search {
// Criando URL de requisição
NSString *query =
[search stringByAddingPercentEscapesUsingEncoding:
NSUTF8StringEncoding];
NSString *urlString = [NSString stringWithFormat:
TEMPLATE_URL, query, API_KEY];
// Preparando request
NSURLRequest *request = [[NSURLRequest alloc]
initWithURL:[NSURL URLWithString:urlString]];
// Declarando o Block "trataResposta" para tratar
// retorno. Esse block é uma função que retorna
// void e recebe 3 parâmetros: uma response, os
// dados e um erro.
id trataResposta = ^(NSURLResponse *response,
NSData *data, NSError *error) {
if (error){
[self showErrorDialog];
} else {
[self trataResposta:data];
}
};
// Fazendo a requisição assícrona para evitar que
// a tela pareça estar travada. Aqui usamos o
// recurso de blocks, declarado acima e que será
// chamado após a requisição ser concluída
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:trataResposta];
}
- (void) trataResposta:(NSData *)data {
NSString *locationString = [[NSString alloc]
initWithData:data encoding:NSUTF8StringEncoding];
// Nesse array, o primeiro elemento é o código de
// resposta HTTP (200 é sucesso) os próximos (se
// deu tudo certo) é a latitude e longitude.
NSArray *listItems = [locationString
componentsSeparatedByString:@","];
double longitude = 0;
double latitude = 0;
if ([listItems count] > 0 &&
[[listItems objectAtIndex:0]
isEqualToString:@"200"]){
latitude = [[listItems objectAtIndex:2]
doubleValue];
longitude = [[listItems objectAtIndex:3]
doubleValue];
}
// Cria o objeto location fazer obter informações
// do local via GeoCoder
CLLocation *location = [[CLLocation alloc]
initWithLatitude:latitude longitude:longitude];
[self retrieveGeocoder:location];
}
- (void) retrieveGeocoder:(CLLocation *)location {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
// Mais uma chamada assícrona que recebe
// um block como parâmetro
[geocoder reverseGeocodeLocation:location
completionHandler:
^(NSArray *placemarks, NSError *error) {
if ([placemarks count] > 0){
CLPlacemark *placemark =
[placemarks objectAtIndex:0];
[self addAnnotation:location.coordinate
withMark:placemark];
} else {
[self showErrorDialog];
}
}];
}
- (void)showErrorDialog {
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:@"Erro"
message:@"Erro ao obter dados da localização"
delegate:nil cancelButtonTitle:@"OK"
otherButtonTitles:nil, nil];
[alert show];
}
- (void)addAnnotation:
(CLLocationCoordinate2D)coordinate
withMark:(CLPlacemark *)placemark {
// Se já houver uma marcação no mapa, remova
if (self.endereco != nil) {
[mapa removeAnnotation:self.endereco];
self.endereco = nil;
}
// Criando MKNotation para adicionar no Mapa
self.endereco = [[Endereco alloc]
initWithCoordinate:coordinate];
// Configura o título e subtítulo da MKNotation
// baseado no CLPlacemark
self.endereco.titulo = [NSString stringWithFormat:
@"%@, %@\n%@",
placemark.locality,
placemark.administrativeArea,
placemark.postalCode];
self.endereco.subtitulo =
[NSString stringWithFormat:@"%@, %f,%f",
placemark.thoroughfare,
coordinate.latitude,
coordinate.longitude];
// Adiciona a notação ao mapa
[self.mapa addAnnotation:self.endereco];
/* A MKCoordinateSpan representa a quantidade
de área usada pela view e é usada para
determinar o zoom. Quanto maior o valor,
maior a área visível. */
MKCoordinateSpan span;
span.latitudeDelta = 0.02;
span.longitudeDelta = 0.02;
// A MKCoordinateRegion serve para movimentar o
// mapa até a região determinada no MKNotation
MKCoordinateRegion region;
region.center = coordinate;
region.span = span;
[self.mapa setRegion:region animated:YES];
[self.mapa regionThatFits:region];
}
#pragma mark - MapView
-(MKAnnotationView *)mapView:(MKMapView *)mapView
viewForAnnotation:(id<MKAnnotation>)annotation {
/* Ao adicionar uma notação no mapa, esse método
é chamado. É aqui que criamos o pin que aparece
no mapa */
MKPinAnnotationView *pin =
[[MKPinAnnotationView alloc] initWithAnnotation:
annotation reuseIdentifier:@"currentLoc"];
pin.pinColor = MKPinAnnotationColorGreen;
pin.animatesDrop = TRUE;
pin.canShowCallout = YES;
pin.calloutOffset = CGPointMake(-5, 5);
return pin;
}
@end
O código está comentado, qualquer dúvida, deixe seus comentários. Agora abra o XIB referente a essa tela e adicione uma SearchBar, um MapView e um SegmentedControl de modo que ele fique conforme a figura abaixo:
No SearchBar, ligue-o com a propriedade searchBar e sete o delegate. Para o MapView, faça o mesmo com a propriedade mapa. E por fim, com o SegmentedControl ligue-o com a propriedade segMapType, e no evento Value Changed ligue com o método mapViewChanged.
Agora é só rodar e pesquisar pelos lugares desejados.
Qualquer dúvida, deixem seus comentários.
4br4ç05,
nglauber