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; } @endA 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; } @endO 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