2010-02-17 9 views
41

Ho un'applicazione che deve avere una funzione di ricerca simile come l'applicazione "Mappe" di Apple (inclusa con iPhone, iPod Touch e iPad).Come cercare MKMapView con UISearchBar?

La funzione in questione non dovrebbe essere una cosa difficile da fare, ma sono davvero all'oscuro di come inserire un indirizzo nella barra di ricerca e quindi ottenere le coordinate per quell'indirizzo o qualcosa che possa aiutarmi a fare effettivamente sposta la mappa e il centro in quel luogo.

Voglio dire, che cosa devo interrogare, Apple fornisce un "metodo di ricerca degli indirizzi API"? o ho bisogno di utilizzare direttamente l'API di google maps?

Mi piacerebbe sapere come dovrebbe essere fatto.

risposta

39

Ok, per rispondere alla mia domanda:

come si è detto prima, la cosa migliore da fare è quello di utilizzare l'API di Google Maps, Supporta molti formati, ma per diversi motivi ho scelto di andare con JSON.

Quindi, ecco i passaggi per eseguire una query JSON su Google Maps e ottenere la coordinata della query. Nota che non tutte le convalide corrette sono fatte, questa è solo una Prova di concetto.

1) Scaricare un JSON quadro/libreria per l'iPhone, ci sono diversi, ho scelto di andare con this one, è molto buono e sembra un progetto attivo, oltre a diverse applicazioni comercial sembrano essere ad usarlo. Quindi aggiungilo al tuo progetto (istruzioni here).

2) per interrogare Google Maps per un indirizzo abbiamo bisogno di costruire un URL richiesta come questo: http://maps.google.com/maps/geo?q=Paris+France

questo url, restituirà un oggetto JSON per la query "Paris + France".

3) Codice:

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

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

Dopo gestiamo la ricerca UISearchBar, dobbiamo fare la richiesta 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]; 
} 

Dobbiamo ovviamente poi gestire la risposta del server GoogleMaps (Nota: mancano molte validazioni)

//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]; 
} 

Infine la funzione per ingrandire la mappa, che dovrebbe ormai è una cosa banale.

- (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]; 
} 

Spero che questo aiuti qualcuno, perché la parte JSON è stato un vero e proprio dolore per capire, la biblioteca non è molto ben documentato, a mio parere, ancora è molto buono.

EDIT:

Modificato un nome del metodo da "searchCoordinatesForAddress:" a causa di @Leo domanda. Devo dire che questo metodo è valido come prova del concetto ma se si prevede di scaricare file JSON di grandi dimensioni, sarà necessario aggiungere un oggetto NSMutableData per contenere tutta la query sul server di google.(Ricordo che le query HTTP venire da pezzi.)

+2

domande e risposte fantastiche. molte grazie! – kape123

+0

google geocoding api versione viene aggiornata. http://code.google.com/apis/maps/documentation/geocoding/ – seapy

+0

Ciao, ma non si risolve una situazione in cui non c'è più di un risultato sul dato indirizzo, se sto leggendo il codice a destra. – JakubM

1

È possibile utilizzare il servizio API di Google per ottenere stringhe lat/long da una stringa di ricerca testuale. Assicurati di passare la posizione corrente dell'utente in modo che i risultati siano pertinenti. Leggi le risposte a questa domanda: Search and display business locations on MKMapView

2

Se qualcun altro sta avendo lo stesso problema, ecco il link: https://github.com/stig/json-framework/ scorrere verso il basso per progetto rinominato in SBJson

Inoltre, ecco il codice per ottenere tutti i dati prima che la tua app li usi. Si noti che il metodo delegato "ha ricevuto i dati" mentre aggiunge l'oggetto dati mutabile ai dati scaricati.

ho appena usato MR GANDOS searchCoodinatesMETHOD AS è come funziona bene

- (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]; 
} 

// FASE UNO // QUESTO è importante in quanto crea l'oggetto DATI MUTABLE APPENA si riceve una risposta

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

} 

/// FASE DUE // QUESTO è importante in quanto aggiunge l'oggetto di dati, CON I DATI

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

// FASE TRE ...... // Ora che avete tutti l'uso DATI farne

- (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

Questo link vi aiuta se si cerca una regione.

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

Se si desidera cercare una strada Questo è il link corect

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

Avviso che il 2 ° ? dovrebbe essere &.

+0

mi ci è voluto un po 'per individuare la differenza tra questa stringa, ma "&" invece di "?" è stato estremamente utile, grazie per questo! – pmk

54

Questo forse il metodo più semplice. Usa i server Apple per il geocoding. A volte i server Apple forniscono una risposta migliore di Google. E presto (in IOS 6.1) le mappe di google saranno completamente fuori IOS. Quindi è positivo se l'app rimane all'interno delle funzionalità fornite da 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

Questo approccio è fantastico! – Samui

+0

Attenzione, CLGeocoder è ancora agli inizi e al momento della scrittura sembra non restituire più valori di 'placemark'. Tutto ciò che rimane uguale anche se sembra eclissare l'opzione dell'API di Google Maps in futuro – NSTJ

+1

Questa soluzione funziona bene su iphone 6.0, ma ho avuto problemi con il metodo setRegion su iPhone 5.0 e 5.1. Ho ricevuto la seguente eccezione: "NSInvalidArgumentException", motivo: "Regione non valida ". La soluzione per questo è aggiungere la seguente riga, dopo aver impostato le proprietà della regione: region = [theMapView regionThatFits: region]; – strongmayer

1

versione Swift, adattata per 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) 
    } 
} 

in base alla risposta del user1466453.