Yandex.Maps: Saya pergi ke pengontrol kartu - Saya langsung mendapatkan posisi pengguna (oke, sekarang serius)

Salam lagi!

Baru-baru ini, saya menerbitkan sebuah artikel yang benar-benar penuh dengan cinta untuk Yandex.Maps. Puisi itu Ode. Di sini, pada kenyataannya, dia habr.com/en/post/479102

Setelah memastikan bahwa ada sedikit pecinta puisi di antara programmer, saya memutuskan untuk menerangi situasi dengan cara yang lebih "HABRovsky". Tangkap banyak kode, pemikiran, dan tangkapan layar. Ayo pergi.

gambar

Mari kita mulai lagi.

Tugasnya sepele: ketika Anda memasukkan controller dengan kartu yang Anda butuhkan untuk segera "memeras" poin pengguna (bonus: Anda juga perlu mendapatkan alamat dalam bentuk yang dapat dibaca bersama dengan semua atibutes yang tersedia).

Kami memotong analis: "Membagi dan menaklukkan."

Untuk mencapai tujuan ini, perlu untuk memecahkan sejumlah masalah teknis dan bisnis, yaitu:

0. Buka pengontrol dengan modul potensial untuk bekerja dengan geo-positioning (MRSG), panggilan balik, dll.

1. IWG
1.1 Menerapkan IWG
1.2 Luncurkan IWG
1.2.1 Dapatkan koordinat pengguna
1.2.2 Lihatlah dia
2 *. Dapatkan alamat posisi dalam format yang mudah dibaca.

Beralih ke pengontrol (VIPER + Configurator)

extension AddPresenter: AddPresentationLogic { // ... func openMap(_ delegate: YandexMapSetPointViewControllerProtocol?) { router.routeTo(target: .addPlace(delegate)) } // ... } 

Delegasi hanya memiliki satu fungsi sehingga ketika Anda menentukan titik yang diinginkan, Anda dapat mengembalikannya melalui protokol ke controller yang memanggil controller dengan kartu:

 protocol YandexMapSetPointViewControllerProtocol { func didSelectPoint(_ place: Place) } 

Kepada delegasi, kami mengirimkan bagian kontrol dari kode panggilan. Segalanya tampak jelas di sini.

Ini adalah betapa bersahaja kita pergi ke controller ...

gambar

Garis silang di tengah terletak persis di tengah pengontrol dan di tengah peta UIView. Asumsi bahwa memperbesar di dalam peta akan berfungsi secara default di tengah jendela. Dan ternyata.

Ke kiri dan tepat di bawah garis bidik - UILabel. Direncanakan untuk menampilkan alamat yang dapat dibaca di sana. Tombol kanan dengan UIActivityIndicator. Intinya adalah bahwa sampai koordinat pengguna telah tiba, ia "berputar" dan tombolnya menjadi gelap dan dinonaktifkan. Dengan mengklik tombol dengan koordinat yang diterima pengguna, kami mengembalikan crosshair kepadanya. Ini merupakan indikasi posisi, mulai dari posisi pengguna.

Di bagian bawah adalah tombol "Select Point". Dengan mengklik, keajaiban logika bisnis terjadi:

 @IBAction func selectButtonWasPressed(_ sender: Any) { let place = Place() place.name = " " place.point.latitude = "\(String(describing: selectedPoint!.latitude))" place.point.longitude = "\(String(describing: selectedPoint!.longitude))" place.addressText = selectedPointGeocoderedAddress delegate?.didSelectPoint(place) navigationController?.popViewController(animated: true) } 

Hore! Kami membahas fase persiapan!

Kami melanjutkan ke MRSG.

Di bawah ini adalah teks yang diformat tabel yang secara pribadi mencerminkan penilaian saya (dengan elemen fuzzy) yang paling terkenal (pada saat itu saya tidak tahu tentang www.openstreetmap.org , terima kasih, daglob ) modul peta bawaan .



“Karena tugasnya sederhana, saya menggunakan Yandex.Maps. Mereka cantik, gesit ... "- Saya pikir.

Jika Anda tertarik untuk mengonfigurasi ini, tulis proyek mini, tetapi jika Anda terlalu malas - tech.yandex.ru/maps/mapkit/?from=mapsapi , dapatkan di jalur jalanku. Memulai itu mudah.

Faktanya adalah bahwa dokumentasi disajikan dalam bentuk ini:



Perhatikan deskripsi yang sedikit dan daftar besar objek di sebelah kiri. Sialan kakimu.
"Mungkin proyek uji akan menjawab pertanyaan saya." Baik, baik.

Inilah binatang buas ini. github.com/yandex/mapkit-ios-demo
Saya tidak melihat solusi untuk tugas sepele saya di sana.
- Oke, - saya pikir - Saya punya cukup pengalaman, jika saya bukan pengembang.



Saya menyusun proyek pengujian dan melihat fitur menyesuaikan penanda pengguna untuk waktu yang lama.

Poin-poin penting:
Ada sebuah objek:

 @IBOutlet weak var mapView: YMKMapView! //  YMKUserLocationObjectListener - ,   

"Semuanya tampak logis," katamu. Tapi tidak. Metode alternatif:

  func onObjectAdded(with view: YMKUserLocationView) {} func onObjectRemoved(with view: YMKUserLocationView) {} func onObjectUpdated(with view: YMKUserLocationView, event: YMKObjectEvent) {} 

kita mendapat kesempatan untuk sampai ke point.lat dan point.long dalam cara yang sangat sulit.
Misalnya, seperti ini:

 userLocation = YMKPoint(latitude: view.pin.geometry.latitude, longitude: view.pin.geometry.longitude) 

Waktu tunggu untuk memuat koordinat bervariasi dengan pendekatan ini dari 2 hingga 50 detik.

"Kau salah, harus ada LocationManager yang fokus," kataku pada diri sendiri. Ternyata kemudian - memang, "teman" seperti itu ada di dokumentasi ... TAPI DI MANA CONTOH DENGAN DIA?!?
Dalam proyek sampel, tidak ada contoh penerapan manajer seperti itu:



- Nah, dokumentasi, hanya Anda dan saya yang tersisa.
- Ya, tidak ada masalah, "inovator", nikmati:



Kami berlangganan UIViewController ke protokol (saya harap tidak perlu menjelaskan lebih lanjut apa pun di sini, well, sungguh, teman-teman):

 // MARK: - // Params var userLocation: YMKPoint? { didSet { guard userLocation != nil && userLocation?.latitude != 0 && userLocation?.longitude != 0 else { return } if isItFirstSelection { isItFirstSelection = false selectedPoint = userLocation mapView.mapWindow.map.move( with: YMKCameraPosition.init(target: userLocation!, zoom: 16, azimuth: 0, tilt: 0), animationType: YMKAnimation(type: YMKAnimationType.smooth, duration: 1), cameraCallback: nil) } activityIndicator.stopAnimating() } } // MARK: - // Some like didLoad setupLocationManager() // MARK: - // Setup private func setupLocationManager() { locationManager = YMKMapKit.sharedInstance()!.createLocationManager() locationManager.subscribeForLocationUpdates(withDesiredAccuracy: 0, minTime: 10, minDistance: 0, allowUseInBackground: true, filteringMode: .on, locationListener: self) } // MARK: - // MARK: YMKLocationDelegate extension YandexMapSetPointViewController: YMKLocationDelegate { func onLocationUpdated(with location: YMKLocation) { userLocation = YMKPoint(latitude: location.position.latitude, longitude: location.position.longitude) } func onLocationStatusUpdated(with status: YMKLocationStatus) {} } 

Dan ...



1-15 detik, CARL! 15! Kadang-kadang itu berhasil opsi sebelumnya lebih cepat! Bagaimana bisa ??
Yandex, lelucon apa? Begitu banyak waktu yang dihabiskan untuk mencoba semuanya, dan untuk mendapatkan hasil seperti itu - yah, umumnya menyedihkan.

Saya berpikir, berpikir ... Yah, tidak benar-benar sama. Berikan seseorang pengontrol dengan kartu dan buat dia dalam keadaan pingsan ketika beralih ke kartu itu selama lebih dari 4 detik - ini adalah bunuh diri untuk aplikasi. Tidak ada yang akan menunggu lebih dari 5 detik dengan keyakinan penuh bahwa ini nyaman (jika Anda tidak percaya, dengarkan laporan Vitaliy Fridman tentang UI / UX).

Saya berpikir lebih banyak ... dan emosi berikut adalah ini:


Siapa yang mau dengan suara - www.youtube.com/watch?v=pTZaNHZGsQo

Resep untuk sukses adalah ini:
Ambil satu kilo ... CLLocationManager dan YMKLocationManager dan ... buat mereka bekerja sama.



Gabungan ini ... "bekerja" terlihat seperti ini:

 // Params private var locationManager: YMKLocationManager! private var nativeLocationManager = CLLocationManager() // MARK: - // Some like didLoad setupNativeLocationManager() // MARK: - // Setup private func setupNativeLocationManager() { if CLLocationManager.locationServicesEnabled() { nativeLocationManager.delegate = self nativeLocationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters nativeLocationManager.startUpdatingLocation() } } // MARK: - // CLLocationManagerDelegate extension YandexMapSetPointViewController: CLLocationManagerDelegate { func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { userLocation = YMKPoint(latitude: locations.last!.coordinate.latitude, longitude: locations.last!.coordinate.longitude) } } 


... dan pilaf siap

Hasil kecepatan mendapatkan poin pengguna: sedikit lebih dari 0 detik.

Kasus ketika (menurut saya) ketika memecahkan masalah sepele, oposisi dari bagian asli dan tertanam tampak seperti ini:



Geocoder *
Sebagai bonus, saya akan menunjukkan implementasi geocoder.
Yandex Geocoder bekas tech.yandex.ru/maps/geocoder

 // Params private let geocoderManager = GeocoderManager.shared //      .     import Foundation import Alamofire import Alamofire_SwiftyJSON import SwiftyJSON import PromiseKit import UIKit // MARK: - // MARK: GeocoderManager class GeocoderManager { static let shared = GeocoderManager() private init() {} func getAddressBy(latitude: String, longitude: String, completion: @escaping (Bool, String?, Error?)->()) { GeocoderAPI().request(latitude: latitude, longitude: longitude).done { (response) in completion(true, response.getAddress(), nil) print("success") }.catch { (error) in completion(false, nil, error) } } } // import Foundation import Alamofire import PromiseKit // MARK: - // MARK: Request enum GeocoderRequest { case addressRequest(String, String) } // MARK: - // MARK: GeocoderRequest extension GeocoderRequest: DefaultRequest { var path: String { switch self { case .addressRequest: return "1.x/" } } var method: HTTPMethod { switch self { case .addressRequest: return .get } } var headers: HTTPHeaders { return [:] } var parameters: [String: Any]? { switch self { case .addressRequest(let latitude, let longitude): return [ "apikey" : Consts.APIKeys.yandexGeocoderKey, "format" : "json", "results" : 1, "spn" : "3.552069,2.400552", "geocode" : "\(longitude),\(latitude)" ] } } func asURLRequest() throws -> URLRequest { let url = try GlobalConsts.Links.geocoderBaseURL.asURL()// not good, need new idea for this var urlRequest = URLRequest(url: url.appendingPathComponent(path)) urlRequest.httpMethod = method.rawValue urlRequest.allHTTPHeaderFields = headers switch method { case .get: urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters) case .post: urlRequest = try JSONEncoding.default.encode(urlRequest, with: parameters) case .put: urlRequest = try JSONEncoding.default.encode(urlRequest, with: parameters) case .patch: urlRequest = try JSONEncoding.default.encode(urlRequest, with: parameters) case .delete: urlRequest = try JSONEncoding.default.encode(urlRequest, with: parameters) default: break } return urlRequest } } 

Sumber daya ini banyak membantu untuk mengubah respons dari server ke model: app.quicktype.io

Hasil pekerjaan secara visual adalah sebagai berikut:



Ringkas hal di atas:
Rekan-rekan, artikel ini ditulis sehingga ketika memecahkan masalah seperti itu Anda tidak menghabiskan waktu sebanyak yang saya habiskan dan baik memilih jalan yang berbeda atau berjalan dengan cepat.

Saya ingin melihat kritik konstruktif dan / atau alternatif solusi yang benar .

Jika artikel tersebut ternyata bermanfaat bagi Anda, koreksi saya dengan peringkat Lois dan, lebih baik, periksa versi asli cerita ini dengan cara yang baru.

Semua kesuksesan kreatif dan suasana hati yang positif!

Source: https://habr.com/ru/post/id479432/


All Articles