Під час розробки iOS-додатку для автомобільного проекту http://megasos.com ми зіткнулися з необхідністю інтеграції Google Maps в iOS. Це виявилося не так тривіально, адже починаючи з версії iOS 6.0 Apple прибрала Google Maps з набору додатків за замовчуванням. Стандартний MapKit також був повністю перероблений і адаптований для роботи з новим картографічним сервісом Apple.
Я не буду вдаватися в усі плюси і мінуси цієї зміни, але найпомітнішим недоліком, характерним для будь-якого нового картографічного сервісу, є недостатньо повне відображення всіх об'єктів (або навіть повна відсутність цих об'єктів).
Існує як мінімум три варіанти інтеграції Карт Google в iOS 6:
- Використання стандартного MapKit з відображенням карт Google в режимі накладання;
- Використання GoogleMapsSDK;
- Розробка власного MapKit з використанням GoogleMaps API.
Перший варіант хороший тим, що не вимагає великих зусиль - завантаження оверлею реалізується всього декількома рядками коду і немає необхідності переробляти процес виведення об'єктів на карту. Але він має і недоліки, основним з яких є зниження продуктивності та значне збільшення споживання трафіку (адже завантажуються обидві карти).
Не забувайте про обмеження на кількість запитів (25 000 запитів на добу безкоштовно, а далі по $0,5 за кожну тисячу).
Другий варіант виявився набагато оптимальнішим з точки зору споживаного трафіку і продуктивності. Але тут зовсім інший процес виведення «анотацій» (в Google Maps вони називаються «маркерами»). Тому на адаптацію додатку знадобиться значно більше часу. Також досить серйозним недоліком стала відсутність готових рішень для кластеризації маркерів і необхідність реалізовувати цей функціонал з нуля.
Третій варіант здався мені найбільш трудомістким, і ми відразу від нього відмовилися.
Використання GoogleMapsSDK
Перш за все вам потрібно створити ключ для додатку в Консолі Google API. Потім завантажте фреймворк з сайту для розробників Google і додайте його в проект (а разом з ним ще з десяток залежних фреймворків).
Після цього створюємо приклад класу GMSMapView, виводимо його в головному View і милуємося картами Google в нашому додатку!
Цей процес детально описано в документації GoogleMapsSDK для 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 можна знайти багато різних реалізованих рішень).
Більш детально про кластеризацію на картах я напишу трохи пізніше.
Кастомне інформаційне вікно (Callouts)
Вміст інфо-вікна можна організувати за допомогою методу:
Вміст інфо-вікна можна організувати за допомогою методу
(UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(id)marker
Але є ще одна проблема, з якою я зіткнувся при переході з MapKit на GoogleMapsSDK - це недостатня інтерактивність цих вікон.
GMSMapView додає не презентацію, згенеровану цією функцією, а її вихідне зображення. Тобто, ви не можете змінити його зовнішній вигляд або обробити подію, пов'язану з певним елементом, що знаходиться на ньому.
Було знайдено наступне рішення: Я додав представлення інформаційного вікна для активного маркера поверх представлення GMSMapView. Єдиним недоліком тут є необхідність постійно змінювати його положення при виклику didChangeCameraPosition
, щоб воно не відставало від маркера.