Как искать MKMapView с помощью UISearchBar?

У меня есть приложение, в котором должна быть аналогичная функция поиска, такая как приложение Apple «Карты» (входит в состав iPhone, iPod Touch и iPad).

Рассматриваемая функция не должна быть сложной задачей, но я действительно не знаю, как ввести адрес в строке поиска, а затем получить координаты для этого адреса или что-то, что может помочь мне фактически переместить карту и центр в этом месте.

Я имею в виду, что мне нужно запросить, предоставляет ли Apple «метод API поиска адресов»? или мне нужно напрямую использовать API карт Google?

Я хотел бы услышать, как это сделать.


person Goles    schedule 17.02.2010    source источник


Ответы (6)


Это, пожалуй, самый простой способ. Он использует серверы Apple для геокодирования. Иногда серверы Apple обеспечивают лучший ответ, чем серверы Google. И скоро (в IOS 6.1) карты Google полностью перестанут работать в IOS. Так что хорошо, если приложение останется в рамках предоставленных функций 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];
    }];
}
person Shakir Zareen    schedule 19.06.2012
comment
Помните, что CLGeocoder все еще находится в зачаточном состоянии и на момент написания, похоже, не возвращает несколько значений placemark. При этом все остается равным, хотя в будущем он, похоже, затмит возможность использования Google Maps API. - person NSTJ; 28.09.2012
comment
Это решение отлично работает на iphone 6.0, но у меня были проблемы с методом setRegion на iphone 5.0 и 5.1. Я получил следующее исключение: 'NSInvalidArgumentException', причина: 'Недействительный регион ‹центр: +40.77026350, -73.97918700 диапазон: +0.12243735, -1.99343872›'. Решение для этого - добавить следующую строку после установки свойств региона: region = [theMapView regionThatFits: region]; - person strongmayer; 02.04.2013
comment
Для метки места FWIW может не быть установлен регион, поэтому приведенный выше код может привести к сбою. Просто используйте другой радиус - скажем, 500 метров - если placemark.region равен нулю. В остальном да, это правильный путь. - person n13; 10.12.2013
comment
В этом ответе используются некоторые устаревшие свойства, к которым нельзя получить доступ в Swift. У кого-нибудь есть современный подход? - person Rikki Gibson; 10.12.2014

Хорошо, чтобы ответить на свой вопрос:

Как упоминалось ранее, лучше всего использовать Google Maps API, он поддерживает множество форматов, но по нескольким причинам я выбрал JSON.

Итак, вот шаги, чтобы выполнить запрос JSON к Google Maps и получить координату запроса. Обратите внимание, что не все правильные проверки выполнены, это только подтверждение концепции.

1) Загрузите фреймворк / библиотеку JSON для iPhone, их несколько, я выбрал это one, он очень хорош и кажется активным проектом, плюс несколько коммерческих приложений, похоже, его используют. Поэтому добавьте его в свой проект (инструкции здесь).

2) Чтобы запросить адрес в Картах Google, нам нужно создать URL-адрес запроса, подобный этому: http://maps.google.com/maps/geo?q=Paris+France

Этот URL-адрес вернет объект JSON для запроса «Париж + Франция».

3) Код:

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

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

После того, как мы обработаем поиск UISearchBar, мы должны сделать запрос в 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];
}

Мы, конечно, должны затем обработать ответ сервера GoogleMaps (Примечание: отсутствует много проверок)

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

Наконец, появилась функция масштабирования моей карты, которая теперь должна быть тривиальной вещью.

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

Надеюсь, это поможет кому-то, потому что часть JSON было настоящей болью, чтобы понять, библиотека, на мой взгляд, не очень хорошо документирована, но все же очень хорошо.

РЕДАКТИРОВАТЬ:

Из-за вопроса @Leo изменено имя одного метода на "searchCoordinatesForAddress:". Я должен сказать, что этот метод хорош как доказательство концепции, но если вы планируете загружать большие файлы JSON, вам придется добавить объект NSMutableData, чтобы хранить все запросы к серверу Google. (помните, что HTTP-запросы поступают по частям.)

person Goles    schedule 18.02.2010
comment
Привет, но вы не решаете ситуацию, когда по заданному адресу есть более одного результата, если я правильно читаю ваш код. - person JakubM; 30.11.2010
comment
Да, это несложно решить, учитывая это решение. - person Goles; 01.12.2010
comment
Обновлена ​​версия API геокодирования Google. code.google.com/apis/maps/documentation/geocoding - person seapy; 24.03.2011
comment
Могу я предложить использовать метод stringByAddingPercentEscapesUsingEncoding: вместо замены пробелов на +? :) - person JakubKnejzlik; 10.07.2012
comment
Да, это было бы лучше, я думаю, сделал это некоторое время назад. :) - person Goles; 11.07.2012
comment
одна рекомендация: используйте http://maps.google.com/maps/geo?q=%@&output=csv (вывод в режиме CSV), чтобы избежать синтаксического анализа JSON. (не нужно полагаться на JSON Framework) - person Raptor; 31.10.2012
comment
Что ж, технически для iOS 5+ парсинг JSON является частью Cocoa-Touch :), так что это тоже не так уж и страшно. - person Goles; 31.10.2012

Эта ссылка поможет вам при поиске в регионе.

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

Если вы хотите найти улицу, это основная ссылка

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

Обратите внимание, что второй ? должен быть &.

person Vali    schedule 29.07.2011
comment
мне потребовалось время, чтобы заметить разницу между этими двумя строками, но & вместо? был чрезвычайно полезным, спасибо за это! - person pmk; 25.02.2013

Версия Swift, адаптированная для 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)
    }
}

на основе ответа user1466453.

person Eric    schedule 16.02.2016

Если у кого-то еще возникла такая же проблема, вот ссылка: https://github.com/stig/json-framework/ прокрутите вниз до Проект переименован в SBJson.

Кроме того, вот код для получения всех данных до того, как ваше приложение будет их использовать. Обратите внимание, что метод делегата «действительно получил данные», поскольку он добавляет изменяемый объект данных к загруженным данным.

Я ПРОСТО ИСПОЛЬЗОВАЛ MR GANDOS searchCoodinatesMETHOD КАК ЕСТЬ КАК ОНА ХОРОШО РАБОТАЕТ

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

// ШАГ ПЕРВЫЙ // ЭТО ВАЖНО, ПОТОМУ ЧТО СОЗДАЕТ ВЛИЯЕМЫЙ ОБЪЕКТ ДАННЫХ СКОРЕЕ ПОЛУЧЕНИЕ ОТВЕТА

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

}

/// ШАГ ВТОРОЙ // ЭТО ВАЖНО, ТАК КАК ОНА ДОБАВЛЯЕТ ОБЪЕКТ ДАННЫХ С ДАННЫМИ

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

// ШАГ ТРЕТИЙ ...... // ТЕПЕРЬ, КОГДА ВЫ ИМЕЕТЕ ВСЕ ДАННЫЕ, ИСПОЛЬЗУЙТЕ ЕГО

- (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
}
person timv    schedule 23.06.2011

Вы можете использовать службу API Google, чтобы получить координаты широты и долготы из текстовой поисковой строки. Обязательно передайте текущее местоположение пользователя, чтобы результаты были релевантными. Прочтите ответы на этот вопрос: Поиск и отображение местоположений предприятий в MKMapView

person William Denniss    schedule 17.02.2010