2010-02-17 29 views
41

Tengo una aplicación que necesita tener una función de búsqueda similar, como la aplicación "Mapas" de Apple (incluida con iPhone, iPod Touch y iPad).¿Cómo buscar MKMapView con UISearchBar?

La característica en cuestión no debería ser una tarea difícil, pero realmente no tengo idea de cómo ingresar una Dirección en la barra de búsqueda, y luego obtener coordenadas para esa dirección o algo que me puede ayudar a mueve el mapa y céntrate en ese lugar.

Es decir, ¿qué tengo que consultar, Apple proporciona un "método API de búsqueda de direcciones"? o necesito usar la API de google maps directamente?

Me encantaría saber cómo se debe hacer.

Respuesta

39

Ok, para responder a mi propia pregunta:

Como se mencionó antes, lo mejor que puede hacer es utilizar la API de Google Maps, Soporta una gran cantidad de formatos, pero por varias razones elegí ir con JSON.

Aquí están los pasos para realizar una consulta JSON a Google Maps y obtener la coordenada de la consulta. Tenga en cuenta que no todas las validaciones correctas están hechas, esto es solo una Prueba de concepto.

1) Descargue un framework/biblioteca JSON para el iPhone, hay varios, elegí ir con this one, es muy bueno y parece un proyecto activo, además de que varias aplicaciones comerciales parecen estar usándolo. Así que agréguelo a su proyecto (instrucciones here).

2) Para consultar mapas de Google para obtener una dirección que necesitamos para construir una URL de solicitud de esta manera: http://maps.google.com/maps/geo?q=Paris+France

esta URL, devolverá un objeto JSON para la consulta "París Francia +".

3) Código:

//Method to handle the UISearchBar "Search", 
- (void) searchBarSearchButtonClicked:(UISearchBar *)theSearchBar 
{ 
    //Perform the JSON query. 
    [self searchCoordinatesForAddress:[searchBar text]]; 

    //Hide the keyboard. 
    [searchBar resignFirstResponder]; 
} 

Después manejamos la búsqueda UISearchBar, tenemos que hacer la solicitud a Google Maps:

- (void) searchCoordinatesForAddress:(NSString *)inAddress 
{ 
    //Build the string to Query Google Maps. 
    NSMutableString *urlString = [NSMutableString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@?output=json",inAddress]; 

    //Replace Spaces with a '+' character. 
    [urlString setString:[urlString stringByReplacingOccurrencesOfString:@" " withString:@"+"]]; 

    //Create NSURL string from a formate URL string. 
    NSURL *url = [NSURL URLWithString:urlString]; 

    //Setup and start an async download. 
    //Note that we should test for reachability!. 
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url]; 
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; 

    [connection release]; 
    [request release]; 
} 

Debemos, por supuesto, a continuación, manejar la respuesta del servidor de GoogleMaps (Nota: una gran cantidad de validaciones faltante)

//It's called when the results of [[NSURLConnection alloc] initWithRequest:request delegate:self] come back. 
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
{ 
    //The string received from google's servers 
    NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 

    //JSON Framework magic to obtain a dictionary from the jsonString. 
    NSDictionary *results = [jsonString JSONValue]; 

    //Now we need to obtain our coordinates 
    NSArray *placemark = [results objectForKey:@"Placemark"]; 
    NSArray *coordinates = [[placemark objectAtIndex:0] valueForKeyPath:@"Point.coordinates"]; 

    //I put my coordinates in my array. 
    double longitude = [[coordinates objectAtIndex:0] doubleValue]; 
    double latitude = [[coordinates objectAtIndex:1] doubleValue]; 

    //Debug. 
    //NSLog(@"Latitude - Longitude: %f %f", latitude, longitude); 

    //I zoom my map to the area in question. 
    [self zoomMapAndCenterAtLatitude:latitude andLongitude:longitude]; 

    [jsonString release]; 
} 

Por último la función para ampliar mi mapa, que debe por ahora, es algo trivial.

- (void) zoomMapAndCenterAtLatitude:(double) latitude andLongitude:(double) longitude 
{ 
    MKCoordinateRegion region; 
    region.center.latitude = latitude; 
    region.center.longitude = longitude; 

    //Set Zoom level using Span 
    MKCoordinateSpan span; 
    span.latitudeDelta = .005; 
    span.longitudeDelta = .005; 
    region.span = span; 

    //Move the map and zoom 
    [mapView setRegion:region animated:YES]; 
} 

Espero que esto ayude a alguien porque la parte JSON fue un verdadero dolor de averiguar, la biblioteca no está muy bien documentado en mi opinión, todavía es muy buena.

EDIT:

Modificado un nombre de método para "searchCoordinatesForAddress:" debido a @Leo cuestión. Debo decir que este método es bueno como una prueba de concepto, pero si planea descargar grandes archivos JSON, tendrá que adjuntar a un objeto NSMutableData para mantener toda la consulta en el servidor de google.(Recuerda que las consultas HTTP vienen por piezas.)

+2

preguntas y respuestas increíbles. ¡muchas gracias! – kape123

+0

se actualiza la versión de la API de google geocoding. http://code.google.com/apis/maps/documentation/geocoding/ – seapy

+0

Hola, pero no resuelves una situación cuando hay más de un resultado en una dirección dada si estoy leyendo correctamente tu código. – JakubM

1

Puede usar el servicio de API de Google para obtener coordenadas lat/long a partir de una cadena de búsqueda textual. Asegúrese de pasar la ubicación actual del usuario para que los resultados sean relevantes. Leer las respuestas a esta pregunta: Search and display business locations on MKMapView

2

Si alguien más está teniendo el mismo problema, aquí está el enlace: https://github.com/stig/json-framework/ abajo desplazarse a Proyecto renombrado a SBJson

Además, aquí está el código de obtener todos los datos antes de que su aplicación lo use. Tenga en cuenta que el método de delegado 'recibió datos' ya que agrega el objeto de datos mutable con los datos descargados.

acabo de utilizar MR Gandos searchCoodinatesMETHOD COMO ES COMO FUNCIONA BIEN

- (void) searchCoordinatesForAddress:(NSString *)inAddress 
{ 
    //Build the string to Query Google Maps. 
    NSMutableString *urlString = [NSMutableString stringWithFormat:@"http://maps.googleapis.com/maps/api/geocode/json?address=%@&sensor=false",inAddress]; 

    //Replace Spaces with a '+' character. 
    [urlString setString:[urlString stringByReplacingOccurrencesOfString:@" " withString:@"+"]]; 

    //Create NSURL string from a formate URL string. 
    NSURL *url = [NSURL URLWithString:urlString]; 

    //Setup and start an async download. 
    //Note that we should test for reachability!. 
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url]; 

    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; 

    [connection release]; 
    [request release]; 
} 

// PASO // este es importante porque crea el objeto de datos MUTABLE EN CUANTO se recibe una respuesta

-(void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response 
{ 
    if (receivedGeoData) 
    { 
     [receivedGeoData release]; 
     receivedGeoData = nil; 
     receivedGeoData = [[NSMutableData alloc] init]; 
    } 
    else 
    { 
     receivedGeoData = [[NSMutableData alloc] init]; 
    } 

} 

/// PASO DOS // éste es importante, ya que añade el objeto de datos CON LOS DATOS

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
{ 
    [receivedGeoData appendData:data]; 
} 

// PASO TRES ...... // Ahora que tiene TODO EL USO DE DATOS DE MAKE IT

- (void)connectionDidFinishLoading:(NSURLConnection *)connection 
{ 
    NSString *jsonResult = [[NSString alloc] initWithData:receivedGeoData encoding:NSUTF8StringEncoding]; 
    NSError *theError = NULL; 
    dictionary = [NSMutableDictionary dictionaryWithJSONString:jsonResult error:&theError]; 

    NSLog(@"%@",dictionary); 

    int numberOfSites = [[dictionary objectForKey:@"results"] count]; 
    NSLog(@"count is %d ",numberOfSites);  
} 

-(void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error 
{ 
    // Handle the error properly 
} 
2

Este enlace le ayuda si se busca una región.

NSMutableString *urlString = [NSMutableString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@?output=json",inAddress]; 

Si desea buscar una calle Este es el enlace corect

NSMutableString *urlString = [NSMutableString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@&output=json",inAddress]; 

Aviso que el segundo ? debería ser &.

+0

me llevó un tiempo detectar la diferencia entre estas dos cuerdas, pero el "&" en lugar de "?" fue extremadamente útil, ¡gracias por eso! – pmk

54

Este es quizás el método más fácil. Utiliza servidores Apple para geocodificar. A veces, los servidores Apple proporcionan una mejor respuesta que google. Y pronto (en IOS 6.1), los mapas de Google estarán completamente fuera de iOS. Por lo tanto, es bueno si la aplicación permanece dentro de las características proporcionadas por Apple.

-(void)searchBarSearchButtonClicked:(UISearchBar *)theSearchBar 
{ 
    [theSearchBar resignFirstResponder]; 
    CLGeocoder *geocoder = [[CLGeocoder alloc] init]; 
    [geocoder geocodeAddressString:theSearchBar.text completionHandler:^(NSArray *placemarks, NSError *error) { 
     //Error checking 

     CLPlacemark *placemark = [placemarks objectAtIndex:0]; 
     MKCoordinateRegion region; 
     region.center.latitude = placemark.region.center.latitude; 
     region.center.longitude = placemark.region.center.longitude; 
     MKCoordinateSpan span; 
     double radius = placemark.region.radius/1000; // convert to km 

     NSLog(@"[searchBarSearchButtonClicked] Radius is %f", radius); 
     span.latitudeDelta = radius/112.0; 

     region.span = span; 

     [theMapView setRegion:region animated:YES]; 
    }]; 
} 
+0

¡Este enfoque es increíble! – Samui

+0

Tenga en cuenta que el CLGeocoder todavía está en su infancia, y al momento de escribir parece no devolver múltiples valores 'placemark'. Todas las cosas permanecen iguales aunque parece eclipsar la opción API de Google Maps en el futuro – NSTJ

+1

Esta solución funciona bien en iphone 6.0, pero tuve problemas con el método setRegion en iphone 5.0 y 5.1. Recibí la siguiente excepción: 'NSInvalidArgumentException', razón: 'Región no válida '. La solución para esto es agregar la siguiente línea, después de establecer las propiedades de la región: region = [theMapView regionThatFits: region]; – strongmayer

1

versión Swift, adaptado para iOS 9:

let geocoder = CLGeocoder() 
geocoder.geocodeAddressString(addressString) { (placemarks, error) in 

    if let center = (placemarks?.first?.region as? CLCircularRegion)?.center { 

     let region = MKCoordinateRegion(center: center, span: MKCoordinateSpanMake(0.02, 0.02)) 
     self.mapView.setRegion(region, animated: true) 
    } 
} 

basado en la respuesta de user1466453.