Всех приветствую.
Совсем недавно поступила задача заменить стандартные карты от Apple на Яндекс карты. Ниже немного о Yandex MapKit.
Yandex MapKit — это кроссплатформенная библиотека, которая позволяет использовать возможности Яндекс.Карт в мобильных приложениях для iOS и Android.
Основная проблема заключалась в том, что библиотека Yandex MapKit создана для UIKit, но наша цель поставить все это дело на SwiftUI. Поэтому прибегаем к старой доброй пикче:
Будем считать, что вы получили ключ у Яндекса и установили библиотеку в проект, для ознакомления с установкой прикрепляю ссылку.
Первым делом создаем класс AppDelegate, подробнее как реализовать этот класс в SwiftUI проекте здесь.
Затем идем в таргет проекта -> Info и прописываем Privacy
В классе AppDelegate импортируем библиотеку Яндекса и устанавливаем ваш API ключ из кабинета разработчика Яндекса
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
YMKMapKit.setApiKey("Ваш API-ключ")
YMKMapKit.sharedInstance()
}
Создадим класс LocationManager, в который импортируем следующее:
import Foundation
import CoreLocation
import YandexMapsMobile
import Combine
LocationManager будет отвечать за всю логику взаимодействия с картами, объявим в нем саму карту и приватную переменную manager, которая наследуется от класса CLLocationManager, для работы с местоположением пользователя
class LocationManager: NSObject{
lazy var map : YMKMap = {
return mapView.mapWindow.map
}()
private let manager = CLLocationManager()
let mapView = YMKMapView(frame: CGRect.zero)
override init(){
super.init()
}
}
Унаследуем класс от протокола CLLocationManagerDelegate и в init() подпишем делегат объявленного менеджера и вызовем у него функции нахождения геопозиции.
class LocationManager: NSObject, CLLocationManagerDelegate
override init(){
super.init()
manager.delegate = self
manager.startUpdatingLocation()
}
Реализуем в классе LocationManager функцию делегата для проверки статуса и начала использования геопозиции
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
self.manager.startUpdatingLocation()
}
}
Затем создадим переменную в которую будем записывать последнее местоположение пользователя:
@Published var lastUserLocation: CLLocation? = nil
Реализуем функцию протокола для того, чтобы слушать изменение локации пользователя и записывать в переменную:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.lastUserLocation = locations.last
}
Приступим написанию UI части. Создадим SwiftUIView с названием MapView и обернем в ZStack. Объявим экземпляр класса LocationManager
@ObservedObject var locationManager = LocationManager()
Так как Yandex MapKit работает с UIKit, создадим структуру YandexMapView и наследуемся от протокола UIViewRepresentable.
Объявим EnviromentObject переменную от класса LocationManager и реализуем функцию makeUIView(), в котором вернем как View ранее созданные mapView в классе LocationManager
import SwiftUI
import YandexMapsMobile
import Combine
struct YandexMapView: UIViewRepresentable {
@EnvironmentObject var locationManager : YaLocationManager
func makeUIView(context: Context) -> YMKMapView {
return locationManager.mapView
}
func updateUIView(_ mapView: YMKMapView, context: Context) {}
}
Вернемся в LocationManager и создадим приватную функцию centerMapLocation, которая на вход будет принимать параметр target, в который будем подавать долготу и широту, и параметр map, который принимает Яндекс карту.
func centerMapLocation(target location: YMKPoint?, map: YMKMapView) {
guard let location = location else { print("Failed to get user location"); return }
map.mapWindow.map.move(
with: YMKCameraPosition(target: location, zoom: 18, azimuth: 0, tilt: 0),
animationType: YMKAnimation(type: YMKAnimationType.smooth, duration: 0.5)
)
}
И теперь реализуем функцию для показа текущей геопозиции пользователя, она будет брать lastUserLocation и передавать в метод centerMapLocation, описанный выше.
func currentUserLocation(){
if let myLocation = lastKnownLocation {
centerMapLocation(target: YMKPoint(latitude: myLocation.coordinate.latitude, longitude: myLocation.coordinate.longitude), map: mapView)
}
}
Объявим в ZStack YandexMapView и передадим ей объявленный класс
ZStack{
YandexMapView()
.edgesIgnoringSafeArea(.all)
.environmentObject(locationManager)
}
Осталось лишь в OnAppear вызвать метод currentUserLocation
.onAppear{
locationManager.currentUserLocation()
}
В результате получаем, работающую Яндекс Карту в вашем SwiftUI проекте
Результат
Ниже прикрепляю код всех классов:
LocationManager
class LocationManager: NSObject, CLLocationManagerDelegate, ObservableObject{
private let manager = CLLocationManager()
let mapView = YMKMapView(frame: CGRect.zero)
@Published var lastUserLocation: CLLocation? = nil
lazy var map : YMKMap = {
return mapView.mapWindow.map
}()
override init() {
super.init()
self.manager.delegate = self
}
func currentUserLocation(){
if let myLocation = lastUserLocation {
centerMapLocation(target: YMKPoint(latitude: myLocation.coordinate.latitude, longitude: myLocation.coordinate.longitude), map: mapView )
}
}
func centerMapLocation(target location: YMKPoint?, map: YMKMapView) {
guard let location = location else { print("Failed to get user location"); return }
map.mapWindow.map.move(
with: YMKCameraPosition(target: location, zoom: 18, azimuth: 0, tilt: 0),
animationType: YMKAnimation(type: YMKAnimationType.smooth, duration: 0.5)
)
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
self.manager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// Notify listeners that the user has a new location
self.lastUserLocation = locations.last
}
}
MapView
struct MapView: View {
@ObservedObject var locationManager = LocationManager()
var body: some View {
ZStack{
YandexMapsView().edgesIgnoringSafeArea(.all).environmentObject(locationManager)
}.onAppear{
locationManager.currentUserLocation()
}
}
}
YandexMapView
struct YandexMapView: UIViewRepresentable {
@EnvironmentObject var locationManager : YaLocationManager
func makeUIView(context: Context) -> YMKMapView {
return locationManager.mapView
}
func updateUIView(_ mapView: YMKMapView, context: Context) {}
}
В следующей части будет реализован поиск по адресу, позиция на карте, выбранного адреса и обновление адреса по координатам из центра камеры.
Всем спасибо за внимание.