Несколько местоположений на карте (с использованием MKMapItem и CLGeocoder)

Я пытаюсь отобразить несколько местоположений в MKMapItem. Я получаю эти местоположения от CLGeocoder, к сожалению, он принимает только одно местоположение. Несмотря на то, что я передаю NSArray, он просто возвращает одно местоположение.

Следующее отлично работает с одним местоположением, но не с несколькими. Как я могу геокодировать несколько местоположений?

Class mapItemClass = [MKMapItem class];
if (mapItemClass && [mapItemClass respondsToSelector:@selector(openMapsWithItems:launchOptions:)]) {
    NSArray *addresses = @[@"Mumbai",@"Delhi","Banglore"];

    CLGeocoder *geocoder = [[CLGeocoder alloc] init];
    [geocoder geocodeAddressString:@[addresses] completionHandler:^(NSArray *placemarks, NSError *error) {
        CLPlacemark *geocodedPlacemark = [placemarks objectAtIndex:0];
        MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:geocodedPlacemark.location.coordinate addressDictionary:geocodedPlacemark.addressDictionary];
        MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
        [mapItem setName:geocodedPlacemark.name];

        [MKMapItem openMapsWithItems:@[mapItem] launchOptions:nil];
    }];
}

person Grey Code    schedule 07.01.2013    source источник
comment
привет .. может ли кто-нибудь помочь мне .. Заранее спасибо   -  person Grey Code    schedule 07.01.2013


Ответы (2)


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

Итак, для этого необходимо отправлять отдельные запросы. Но эти запросы (которые выполняются асинхронно) не должны выполняться одновременно. Итак, вопрос в том, как сделать так, чтобы серия асинхронных запросов геокодирования выполнялась последовательно, один за другим.

Есть много разных способов решить эту проблему, но один особенно элегантный подход заключается в использовании параллельного подкласса NSOperation, который не завершает операцию (т. е. не выполняет isFinished КВН) до тех пор, пока не будет получен блок асинхронного завершения запроса геокодирования. называется. (Информацию о параллельных операциях см. в разделе Настройка операций для параллельного выполнения в Очередь операций главы Руководство по программированию с параллельным доступом). Затем просто добавьте эти операции в очередь последовательных операций.

Другой подход заключается в том, чтобы заставить этот асинхронный запрос геокода вести себя синхронно, а затем вы можете просто добавить запросы в последовательную очередь, и запросы будут выполняться последовательно, а не параллельно. Этого можно добиться с помощью семафоров, фактически предписывая отправленной задаче не возвращаться до завершения запроса геокодирования. Вы можете сделать это так:

CLGeocoder *geocoder = [[CLGeocoder alloc]init];
NSMutableArray *mapItems = [NSMutableArray array];

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 1;   // make it a serial queue

NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
    [MKMapItem openMapsWithItems:mapItems launchOptions:nil];
}];

NSArray *addresses = @[@"Mumbai, India", @"Delhi, India", @"Bangalore, India"];

for (NSString *address in addresses) {
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

        [geocoder geocodeAddressString:address completionHandler:^(NSArray *placemarks, NSError *error) {
            if (error) {
                NSLog(@"%@", error);
            } else if ([placemarks count] > 0) {
                CLPlacemark *geocodedPlacemark = [placemarks objectAtIndex:0];
                MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:geocodedPlacemark.location.coordinate
                                                               addressDictionary:geocodedPlacemark.addressDictionary];
                MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
                [mapItem setName:geocodedPlacemark.name];

                [mapItems addObject:mapItem];
            }
            dispatch_semaphore_signal(semaphore);
        }];

        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    }];

    [completionOperation addDependency:operation];
    [queue addOperation:operation];
}

[[NSOperationQueue mainQueue] addOperation:completionOperation];

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

person Rob    schedule 07.01.2013
comment
@GreyCode Я понимаю, что тонкости NSOperationQueue и зависимости объектов NSOperation могут немного сбивать с толку, поэтому я обновил свой ответ, указав более традиционный подход, который не использует NSOperationQueue. Если есть что-то конкретное, что вам нужно объяснить, пожалуйста, дайте мне знать. (Я не уверен, что ваш вопрос.) - person Rob; 08.01.2013
comment
@Rob-Привет, Роб.. я сомневаюсь насчет NSArray.. не могли бы вы присоединиться ко мне. мой почтовый идентификатор: [email protected].. Мне нравится делиться и узнавать больше от вас, мистер Роб.. Спасибо - person Grey Code; 08.01.2013
comment
@Rob-stackoverflow.com/ вопросов/14212664/ Роб.. ты понимаешь мой вопрос?? - person Grey Code; 09.01.2013
comment
@GreyCode Да. Я думаю, вы запутались в [friend.location objectForKey:@"name"], что означает NSString, а не NSArray. Но я покажу вам в своем комментарии к этому другому вопросу, как построить массив из этих строк. - person Rob; 09.01.2013
comment
@Rob-при использовании этого он зависает и отображает ошибку .. также я разместил еще один вопрос .. заголовок stackoverflow.com/questions/14235717/ - person Grey Code; 09.01.2013
comment
Я продолжаю получать *** Завершение приложения из-за необработанного исключения «NSInvalidArgumentException», причина: «-[длина MKMapItem]: нераспознанный селектор отправлен экземпляру 0x7f93f5133640» - person Jordan Brown; 22.08.2014
comment
Спасибо, @Rob. Я попытался поймать его с помощью точки останова исключения, но безуспешно :(. Я только что опубликовал свой вопрос. stackoverflow.com/questions/25438928/ - person Jordan Brown; 22.08.2014
comment
Кстати, здесь я упростил свой ответ, так как предыдущий пример кода был слишком запутанным. - person Rob; 22.08.2014

Для парней, ищущих быстрое решение:

func getCoordinate( addressString : String, completionHandler: @escaping(CLLocationCoordinate2D, NSError?) -> Void ){
            let geocoder = CLGeocoder()
            geocoder.geocodeAddressString(addressString) { (placemarks, error) in
                if error == nil {
                    if let placemark = placemarks?[0] {
                        let location = placemark.location!
                        completionHandler(location.coordinate, nil)
                        return
                    }
                }

                completionHandler(kCLLocationCoordinate2DInvalid, error as NSError?)
            }
        }

Спасибо Apple. Официальный документ ссылка

Вы можете использовать фрагмент следующим образом для нескольких адресов:

let address = ["India","Nepal"]

for i in 0..<address.count {
 getCoordinate(addressString: address[i], completionHandler: { (location, error) in
                //Do your stuff here. For e.g. Adding annotation or storing location
      })

}
person Rakesh Gujari    schedule 20.12.2017
comment
Этот подход не решает проблему параллелизма, поскольку вы не ждете завершения первого запроса перед отправкой второго запроса. - person Felix; 17.01.2019