Внедряем Google Maps в приложение для iOS 6.0

Внедряем Google Maps в приложение для iOS 6.0

Разрабатывая iOS приложение для автомобильного проекта https://megasos.com мы столкнулись с необходимостью использования Google Maps. Это оказалось не совсем тривиальной задачей т.к. начиная с версии iOS 6.0 Apple убрали Google Maps с базового набора приложений. Стандартный MapKit тоже был полностью переделан и адаптирован для работы с новым картографическим сервисом Apple.

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

Внедряем Google Maps в приложение для iOS 6.0

Интеграция Google Maps в приложение на iOS 6 возможна тремя способами:

  • использование стандартного MapKit с прорисовкой карты Google в режиме оверлея;
  • использование GoogleMapsSDK;
  • разработка собственного MapKit с применением GoogleMaps API.

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

Второй вариант оказался гораздо оптимальней с точки зрения потребляемого трафика и производительности. Но здесь совершенно другой процесс вывода "аннотаций" (в Google Maps они называются "маркерами"). Поэтому на адаптацию приложения нужно будет затратить намного больше времени. Также довольно серьезным недостатком оказалось отсутствие готовых решений по кластеризации маркеров и необходимость реализации этого функционала с нуля.

Третий вариант мне показался самым трудоемким, и мы сразу от него отказались.

Использование GoogleMapsSDK

Сначала нужно создать ключ для приложения в Google API's Console. Далее качаем фреймворк с сайта разработчиков Google и добавляем его в проект (а с ним еще с десяток зависимых фреймворков).

После этого создаем экземпляр класса GMSMapView, выводим его в основной View, и любуемся Google картами в нашем приложении!

Этот процесс хорошо описан в документации GoogleMapsSDK for iOS. Пока на карте не очень много маркеров, то все работает довольно шустро :).

Маркеры

С первого взгляда процесс добавления маркеров и управление ними в GoogleSDK выглядит довольно простым. 

В MapKit необходимо создавать класс для протокола MKAnnotation и добавить аннотации на карту. Потом в методе mapView:viewForAnnotation: создать View уже для самого маркера и менять его внешний вид.

В GoogleMapsSDK это делается в несколько строчек:

CLLocationCoordinate2D position = CLLocationCoordinate2DMake(-33.86, 151.20);
GMSMarker * marker = [GMSMarker markerWithPosition:position];
marker.title = @"Sydney";
marker.snippet = @"Australia";
marker.icon = [UIImage @"marker.webp"]
marker.map = self.mapView;

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

Для решения этой проблемы можно прятать маркеры которые не попадают в область видимости в методе mapView:didChangeCameraPosition:. Но это совсем не спасает в тех случаях, когда пользователь максимально увеличивает масштаб (так как все маркеры все равно будут в области видимости). И тут, как и в случае со стандартным MapKit, нужна кластеризация (группировка и объединение маркеров).

Для GoogleMapsSDK придется создавать класс обработки кластеризации маркеров с нуля (в отличии от MapKit, для которого на GitHub можно найти множество разных реализованных решений).

Внедряем Google Maps в приложение для iOS 6.0

Более подробно о кластеризации на картах я напишу чуть позже.

Кастомные InfoWindow (Callouts)

Оформить внешний вид для Info Window можно с помощью метода:

(UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(id)marker

Но есть еще одна проблема, с которой я столкнулся при переходе с MapKit на GoogleMapsSDK — отсутствие интерактивности этих окошек.

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

Решение нашлось следующее: я добавил представление информационного окошка для активного маркера поверх представления GMSMapView. Единственный минус, это необходимость постоянно изменять его координаты при вызове didChangeCameraPosition, чтобы он не оставался в стороне от маркера.