Интеграция Yandex MapKit в SwiftUI проект. Часть 1

от автора

Всех приветствую.

Совсем недавно поступила задача заменить стандартные карты от 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) {} }

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

Всем спасибо за внимание.


ссылка на оригинал статьи https://habr.com/ru/articles/728772/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *