iOS
Maps SDK позволяет добавить карту в ваше iOS приложение.
Для подключения Maps SDK вы можете использовать Swift Package Manager.
Установка с помощью Swift Package Manager
Maps SDK доступен в виде пакета для Swift Package Manager (SPM). SPM — это менеджер зависимостей, он интегрирован в систему сборки Swift и автоматизирует процесс загрузки, компиляции и линковки зависимостей. Для установки пакета выполните следующие действия.
- Откройте проект в Xcode.
- Нажмите File → Add Packages.
Также вы можете выбрать файл проекта на панели Project Navigator, перейти к настройкам проекта, далее выбрать вкладку Package Dependencies и нажать кнопку Add Package Dependency.
В появившемся окне введите адрес репозитория: https://github.com/geors/maps-sdk-ios.
Интеграция карт в приложение
Импортируйте Maps SDK в файл, в котором вы будете использовать карту.
import Maps SDK
Для начала работы с картой выполните следующие действия.
- Создайте объект типа
MapView
. - Сконфигурируйте карту.
- Укажите делегат карты.
Создание объекта MapView
let mapView = MapView()
Конфигурация объекта MapView
Для конфигурации необходимо создать объект типа MapViewConfig
, содержащий следующие сведения:
- API-ключ для работы с SDK;
- координаты центра карты;
- начальный уровень zoom;
- стиль тайлов.
Пример настройки и установки конфигурации
let mapConfig = MapViewConfig (
apiKey: "##API_KEY##",
center: Coordinates (lng: 33, lat: 55),
zoomLevel: 11,
style: .automatic
)
mapView.setup(mapConfig)
Уровень зума (zoomLevel
) может иметь значение от 0
(самый отдаленный) до 22
(самый приближенный). Стиль тайлов (style
) может принять одно из следующих значений:
.automatic
— автоматический выбор стиля в зависимости от isDriveMode и текущей цветовой схемы устройства;.main
— базовый стиль;.light
— светлый стиль;.dark
— темный стиль;.navMain
— базовый стиль для навигации с акцентом на дороги;.navDark
— темный стиль для навигации с акцентом на дороги.
Указание делегата карты
Делегат должен реализовывать требования протокола MapViewDelegate
. Он будет использоваться для обработки оповещений от карты о различных событиях, таких как:
- карта была загружена;
- было получено событие, иницированное пользователем (например касание);
- было получено событие, инициированное элементами управления картой и т. д.
Пример интеграции карты в приложение
import Maps SDK
...
override func viewDidLoad () {
let mapView = MapView ()
let mapConfig = MapViewConfig (
apiKey: "##API_KEY##",
center: Coordinates (lng: 33, lat: 55),
zoomLevel: 11,
style: .automatic
)
mapView.setup(mapConfig)
mapView.delegate = self
view.addSubview(mapView )
}
Использование и управление картой
Установка текущих параметров пользователя
Метод setCurrentLocation
устанавливает:
- текущие координаты пользователя;
- направление, в котором смотрит или движется пользователь (
bearing
); - точность позиции пользователя в метрах (
accuracy
); - уровень зума (
zoom
). Данный аргумент является необязательным.
Установка текущей координаты и направления пользователя
// без установки зума
mapView.setCurrentLocation(Coordinates(lng: 33, lat: 55), bearing: 0, accuracy: 0)
// с установкой зума
mapView.setCurrentLocation(Coordinates(lng: 33, lat: 55), bearing: 0, accuracy: 0, zoom: 10)
Уровень зума (zoom
) может изменяться в диапазоне от 0
(самый отдаленный) до 22
(самый приближенный).
Управление камерой
Для управления камерой используется метод flyTo
, а также setBearing
и setZoom
.
Метод flyTo
Позволяет изменить координаты центра карты. За анимацию изменений отвечает аргумент animated
, а за длительность — duration
.
// без указания длительности анимации
mapView.flyTo(Coordinates(lng: 33, lat: 55), animated: true )
// с указание длительности анимации
mapView.flyTo(Coordinates(lng: 33, lat: 55), animated: true , duration: 2)
Дополнительно вы можете передать аргумент options типа MapCameraOptions
, определяющий:
- азимут (
bearing
) — угол поворота карты; - зум (
zoom
) — уровень зума карты; - отступы (
padding
) — отступы с каждой стороны окна для смещения центральной точки вьюпорта; - кривую движения камеры (
curve
) — коэфициент для определения траектории движения камеры; - флаг сброса режима следования (
resetFollowMode
) см. раздел Установка режимов следования.
Каждый из перечисленных параметров является необязательно.
Пример метода flyTo
с дополнительными опциями MapCameraOptions
.
let options = MapCameraOptions(bearing: 180,
zoom: 12,
padding: UIEdgeInsets.init(top: 20, left: 20, bottom: 150, right: 20),
curve: 1.42,
resetFollowMode: false)
mapView.flyTo(Coordinates(lng: 33, lat: 55), options: options , animated: true )
Установка направления карты
Метод setBearing
устанавливает направление карты и является облегченной версией метода flyTo
.
mapView.setBearing(90, animated: true)
Фиксация севера карты сверху
Свойство isNorthAlwaysUp
запрещает вращать карту любым способом, если установлено в true
. Север будет всегда сверху карты. По умолчанию false
.
mapView.isNorthAlwaysUp = true
Скрытие маркера текущей локации
Свойство isCurrentLocationHidden
позволяет скрывать маркер текущей локации, когда она определена. По умолчанию false
.
mapView.isCurrentLocationHidden = true
Установка уровня зума
Метод setZoom
устанавливает зум карты и является облегченной версией метода flyTo
.
mapView.setZoom(11, animated: true)
Установка режимов следования (followLocation
, followBearingAndLocation
, free
)
mapView.mode = .followLocation
Вписывание области во вьюпорт
mapView.fitBounds(northWest: coords1, southEast: coords2, animated: true)
Дополнительные возможности карты
Установка минимального и максимального уровня зума
mapView.setMinZoom(10, maxZoom: 15)
Включение и выключение элементов управления
mapView.isZoomButtonsHidden = true
mapView.isCompassHidden = true
mapView.isMyLocationButtonHidden = true
Включение и выключение жестов
mapView.isDragPanEnabled = true
mapView.isZoomRotateEnabled = true
Выравнивание логотипа
mapView.logoAlignment =.bottomRight
mapView.logoIgnoresSafeArea = false
mapView.logoInsets =.zero
По умолчанию логотип отображается в нижнем правом углу карты и учитывает safeArea
.
Маркеры
Для создания маркеров используется структура Marker
. В инициализатор передаются уникальный идентификатор маркера, координату и картинку, а также, опционально, выравнивание. Для картинки рекомендуется использовать изображение размером 48х48 пикселей. Можно использовать набор из предоставляемых картинок или использовать свою собственную.
Добавление отдельных маркеров
let marker1 = Marker(id: "marker_id_1",
coords: Coordinates(lng: 33, lat: 55),
pin: .electricPin)
let marker2 = Marker(id: "marker_id_2",
coords: Coordinates(lng: 33, lat: 55),
pin: .electricInfo,
alignment: .bottomLeft)
В данном случае передаются картинки из предустановленного в SDK набора изображений. При необходимости вы можете передать собственное изображение.
let markerImage = UIImage(...)
let marker3 = Marker(id: "marker_id_3",
coords: Coordinates(lng: 33, lat: 55),
pin: .custom (markerImage),
alignment: .center)
По умолчанию выравнивание маркера имеет значение .center
, то есть центр маркера совмещается с переданными координатами. У вас есть возможность гибко управлять этим параметром, выбирая одно из множества доступных значений, или передать произвольное смещение.
// Выравнивание по центру нижней грани
let marker4 = Marker(id: "marker_id_4",
coords: Coordinates(lng: 33, lat: 55),
pin: .electricPhoto ,
alignment: .bottom )
// Выравнивание со смещением
// Смещение высчитывается относительно центра маркера
let marker5 = Marker(id: "marker_id_5",
coords: Coordinates(lng: 33, lat: 55),
pin: .electricStar ,
alignment: .center(offsetByX: 10, byY: -10))
Для размещения маркера на карте используется метод addMarker(_: Marker)
.
Добавление маркеров по одному
mapView.addMarker(marker1)
mapView.addMarker(marker2)
Добавление маркеров массивом
mapView.addMarkers([marker1, marker2])
Удаление отдельного маркера с указанием его идентификатора
mapView.removeMarker(id: "marker_id_1")
Удаление всех маркеров
mapView.removeAllMarkers()
Отслеживание события нажатия на маркер
func MapDelegate: MapViewDelegate {
func mapView(_map: MapView, didSelectMarkerID markerid: String ) {
// ...
// markerID * ID выбранного маркера
// ...
}
}
Дополнительно при нажатии на любой маркер вызывается метод MapViewDelegate.mapView(_: MapView, didReceiveEvent: MapEvent)
делегата. В данном случае свойство didReceiveEvent.type
имеет значение .clickOnMarker
.
func MapDelegate: MapViewDelegate {
func mapView(_map: MapView, didReceiveEvent event: String) {
// ...
// Для события нажатия на маркер
// event.type = .clickOnMarker
// ...
}
}
Кластеризация
Маркеры можно объединять в кластеры. Кластеризация создает новый источник данных на карте. Вы можете указать радиус кластеров в метрах, цвет текста и фона.
func mapViewDidLoad(_mapView: MapView ) {
let markers: [ Marker ] = ...
mapView.addCluster(markers, id: "clusterId", radius: 50, textColor: .white, backgroundColor: .blue)
}
Удаление кластеров происходит с указанием идентификатора кластера.
mapView.removeCluster(id: "clusterId")
Попапы
Для показа попапа на карте нужно указать идентификатор маркера, для которого он будет отображен, а также текст внутри попапа.
mapView.displayPopup(markerId: "marker_id_1", content: "Hello world")
Для скрытия попапа нужно указать идентификатор маркера.
mapView.hidePopup(markerId: "marker_id_1")
Для показа попапа после выбора маркера на карте необходимо реализовать метод mapView(_:, didSelectMarkerID:)
делегата MapViewDelegate
.
extension MyController: MapViewDelegate {
func mapView(_mapView: MapView, didSelectMarkerID id: String) {
mapView.displayPopup(id: id, content: "Hello world")
}
}
Стили
Карта поддерживает смену стилей. Стиль .automatic
означает, что карта будет автоматически менять светлый и темный стили в зависимости от настройки системной темы интерфейса.
mapView.changeStyle(.automatic)
mapView.changeStyle(.dark)
GeoJSON
Карта поддерживает отрисовку полигонов и линий из GeoJSON источника.
Добавление источника данных и отрисовка слоёв
let sourceData = Data(...)
let source = MapDataSource(id: "sourceID", type: .geoJSON(sourceData))
mapView.addSource(source)
let fillLayer = MapLayer (
id: "fillLayer",
sourceid: "sourceID",
paint: FillPaintProperties (fillColor: .iuColor(.green), fillOpacity: .value(0.3))
)
let strokeLayer = MapLayer(
id: "strokeLayer",
sourceid: "sourceID",
paint: LinePaintProperties()
)
mapView.addLayer(fillLayer)
mapView.addLayer(strokeLayer)
Значение цвета и прозрачности можно задать в самом источнике. В таком случае используйте .source("field")
, где field
— имя поля в properties
источника, из которого нужно брать значение. Удалить источники и слои можно с указанием их идентификаторов.
mapView.removeLayer("fillLayer")
mapView.removeLayer("strokeLayer")
mapView.removeSource("sourceID")
)
Поддерживается отрисовка маршрутов в формате кодированной строки из сервиса Построение маршрута.
let routeSource = MapDataSource (id: "routeSourceID", type: .encodedString(encodedRoute ))
mapView.addSource(routeSource)
let routeLayer = MapLayer (
id: "routeLineLayer",
sourceid: "routeSourceID",
paint: LinePaintProperties(lineColor: .iuColor(.red), lineWidth: 2)
)
mapView.addLayer(routeLayer )
Поддерживается отрисовка линий с градиентным переходом цветов на ней. Для этого необходимо включить опцию lineMetrics
у MapDataSource
, создать объект LineGradient
, указав список offset
по длине линии в диапазоне 0.0
... 1.0
и color hex строкой или стандартное имя цвета CSS.
let encodedRoute: String = ...
let routeSourceID = "route"
let routeSource = MapDataSource (
id: routeSourceID ,
type: .encodedString(encodedRoute),
lineMetrics: true
)
let gradient = LineGradient(stops: [
LineGradientStop(offset: 0, color: "#0000FF"),
LineGradientStop(offset: 0.1, color: "royalblue"),
LineGradientStop(offset: 0.3, color: "cyan"),
LineGradientStop(offset: 0.5, color: "lime"),
LineGradientStop(offset: 0.7, color: "yellow"),
LineGradientStop(offset: 1, color: "#FF0000" )
])
let routeLayer = MapLayer (
id: "routeLineLayer",
sourceid: routeSourceID ,
paint: LinePaintProperties (
lineColor: .iuColor (.green),
lineWidth: 6,
lineGradient: gradient
)
)
mapView.addSourcesAndLayers (
sources: [ routeSource ],
layers: [ routeLayer ]
)
Поддерживается отрисовка кругов. Поскольку GeoJSON не поддерживает круги, они симулируются полигоном с заданным количеством сторон.
mapView.addCircleSource(center: coords, radius: 500, steps: 32, id: sourceID)
Если источники (sources
) и слои (layers
) известны заранее, то вы можете добавить их единым вызовом.
mapView.addSourcesAndLayers (
sources: [
source ,
routeSource
],
layers: [
fillLayer ,
strokeLayer ,
routeLayer
]
)
Пробки и изолинии
Карта может показывать пробки на дорогах, линии метро и уровни высот (изолинии). Для включения воспользуйтесь методом setLayoutVisible
.
mapView.setLayoutVisible(true, layout: .traffic)
mapView.setLayoutVisible(true, layout: .isolines)
mapView.setLayoutVisible(true, layout: .isolinesLabel)
mapView.setLayoutVisible(true, layout: .subway)
Обработка ошибок
Для обработки ошибок используется метод mapView(_:, didFailWithError:)
делегата MapViewDelegate
.
extension MyController: MapViewDelegate {
func mapView(_mapView: MapView, didFailWithError error: Error) {
print("Did fail with error: (error.localizedDescription)")
}
}
Геокодирование
Geocoder — компонент Maps SDK для прямого и обратного геокодирования. Для инициализации необходим API_KEY.
Пример прямого геокодирования
let geocoder = Geocoder(apiKey: "your apiKey")
geocoder.geocode (
query: "Ленинградский проспект 39с79",
lang: "ru",
location: Coordinates(lng: 37.537892, lat: 55.796926)
) { result in
switch result {
case let.success(response):
print(response)
case let.failure(error):
print(error.localizedDescription )
}
}
SwiftUI
Для использования карты в SwiftUI
предусмотрен компонент Map
.
Пример использования в SwiftUI
import SwiftUI
import Maps SDK
class StateObject: ObservableObject {
let mapConfig = MapViewConfig (
apiKey: apiKey ,
center: Coordinates(lng: 37.537892, lat: 55.796926)
zoomLevel: 15,
style: .automatic
)
let mapView = MapView()
}
extension StateObject: MapViewDelegate {
// методы делегата
}
struct ContentView: View {
@ StateObject private var state = StateObject()
var body: some View {
Map (
config: state.mapConfig ,
view: state.mapView ,
delegate: state
)
}
}
Ограничения
Maps SDK может быть интегрирован в приложения с поддержкой iOS 12.4 и более поздних версий.
Начиная с iPadOS 13 у пользователей появилась возможность использовать многооконный режим. Maps SDK не был оптимизирован для работы в этом режиме.
Вы можете с легкостью интегрировать Maps SDK в приложения, основанные на фреймворках UIKit или SwiftUI.