Яндекс.Карты в SwiftUI
Этот проект демонстрирует, как интегрировать библиотеку YandexMapKit в приложение на SwiftUI. YandexMapKit позволяет добавлять карты Яндекса в ваше приложение и использовать различные возможности картографических сервисов Яндекса, такие как отображение карт, маршрутизация, геокодирование и другие.
В этом репозитории содержится проект YandexMapKit
(далее в качестве примера будут испоьзованы скрины из этого проета), для демонстрации используется функция преобразования координат.
- iOS 12.0 и выше
- Xcode 12.0 и выше
- Swift 5.3 и выше
Проверьте установлен на вашем Mac: Homebrew, Xcode Command Line Tools и Ruby
Если нет, выполните следующие команды в терминале
Убедитесь, что у вас установлены необходимых инструментов для сборки. Если нет, установите их с помощью следующей команды:
- Установите Xcode Command Line Tools:
$> xcode-select --install
- Установите Homebrew, если он еще не установлен:
$> /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- Установите необходимые пакеты с помощью Homebrew:
$> brew install autoconf automake libtool pkg-config
- Установите Ruby через Homebrew::
$> brew install ruby
1) Установка CocoaPods
$> sudo gem install cocoapods
Проверка
$> pod --version
your_version
Если у вас возникнут какие-либо проблемы во время установки
-
Ознакомьтесь с этим руководством: Документация
-
Мне также помогли следующие действия:
$> sudo gem uninstall cocoapods $> brew reinstall ruby $> sudo gem update --system 3.5.10 $> sudo gem install drb -v 2.0.6 $> sudo gem install cocoapods
2) Получим API ключ для работы
Подробно: Документация
Чтобы получить API ключ: нужно завести аккаунт разработчика Яндекса. Для начала зайдем в кабинет разработчика Яндекса
Авторизуйтесь в свой аккаунт. Нажмите Подключить API и выберите MapKit – мобильный SDK
Заполните информацию о своем проекте. Ваш API-ключ будет успешно создан, вскоре он понадобится.
3) Создаем проект
Создаем проект SwiftUI в XCode: yourProjectSwiftUI
Например: создадим проект YandexMapKit
Далее установим PodFile, для этого в терминале директории проекта:
$> cd yourProjectSwiftUI
$> pod init
$> open Podfile
В открывшемся PodFile добавим зависимость:
use_frameworks!
pod 'YandexMapsMobile', '4.6.1-full'
Для нашего проекта будет выглядеть следующим образом
Выполните в директории проекта команду:
$> pod install
Откройте файл проекта:
$> open *.xcworkspace
Добавим класс AppDelegate в проект, для этого:
-
Создадим файл AppDelegate.swift
-
Добавим следующий код в файл и устанавливаем API ключ:
import SwiftUI import YandexMapsMobile class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { YMKMapKit.setApiKey("Ваш API ключ") YMKMapKit.sharedInstance() return true } }
-
Добавим в проект yourProjectSwiftUI следующую строку:
import SwiftUI @main struct yourProjectSwiftUIApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate var body: some Scene { WindowGroup { ContentView() } } }
-
Добавим в таргет проекта:
Privacy - Location When In Use Usage Description
Надпись (графа String) может быть любой, например: Доступ к вашей локации
В процессе сборки проекта у меня возникала проблема: "Sandbox: rsync.samba (13105)"
Решение: Изменить параметр сборки проекта Xcode
User Script Sandboxing - 'No'
Добавим класс LocationManager и структуру YandexMap
-
Класс LocationManager предназначен для взаимодействия с картой, выполняет роль ViewModel и отвечает за логику управления процессами
import Foundation import CoreLocation import YandexMapsMobile import Combine class LocationManager: NSObject, CLLocationManagerDelegate, ObservableObject { @Published var lastUserLocation: CLLocation? = nil private let manager = CLLocationManager() let mapView = YMKMapView(frame: CGRect.zero)! lazy var map : YMKMap = { return mapView.mapWindow.map }() override init() { super.init() self.manager.delegate = self } func pointLocation(point: YMKPoint) { let cameraPosition = YMKCameraPosition(target: point, zoom: 17.0, azimuth: 150.0, tilt: 30.0) map.move(with: cameraPosition, animation: YMKAnimation(type: .smooth, duration: 1.0)) } }
-
Структура YandexMap предназначена для отображения карты, тк библиотека работает с UIKit, поэтому также требуется выполнение протокола UIViewRepresentable
struct YandexMapView: UIViewRepresentable { @EnvironmentObject var locationManager : LocationManager func makeUIView(context: Context) -> YMKMapView { return locationManager.mapView } func updateUIView(_ mapView: YMKMapView, context: Context) {} }
-
Структура MapView представляет графический интерфейс и является UI элементом (View)
struct MapView: View { @EnvironmentObject var locationManager : LocationManager @Binding var latitude: String @Binding var longitude: String var body: some View { ZStack { YandexMapView() .edgesIgnoringSafeArea(.all) .environmentObject(locationManager) }.onAppear{ locationManager.pointLocation(point: YMKPoint(latitude: Double(latitude) ?? 55.751263550483145, longitude: Double(longitude) ?? 37.62967790286453)) } } }
Создадим ListView для удобства использования MapView и добавим в ContentView:
import SwiftUI
import YandexMapsMobile
import Combine
struct ContentView: View {
@State var latitude = String()
@State var longitude = String()
@ObservedObject var locationManager = LocationManager()
var body: some View {
ZStack{
MapView(latitude: $latitude, longitude: $longitude)
.environmentObject(locationManager)
ListView(latitude: $latitude, longitude: $longitude)
.environmentObject(locationManager)
}
}
}
struct ListView: View {
@EnvironmentObject var locationManager : LocationManager
@Binding var latitude: String
@Binding var longitude: String
var body: some View {
VStack {
Spacer()
HStack {
VStack(spacing: 10) {
TextField("latitude", text: $latitude)
.padding(10)
.background(.white)
.clipShape(Capsule())
TextField("longitude", text: $longitude)
.padding(10)
.background(.white)
.clipShape(Capsule())
}
Button("Start") {
locationManager.pointLocation(point: YMKPoint(latitude: Double(latitude) ?? 55.751263550483145,
longitude: Double(longitude) ?? 37.62967790286453))
}
.padding(25)
.background(.blue.opacity(0.7).gradient)
.clipShape(Circle())
.foregroundStyle(.white)
.font(.system(size: 20, weight: .bold))
}
.padding(20)
.background(Color(cgColor: CGColor(red: 0.3, green: 1, blue: 0.3, alpha: 1)))
}
}
}