Ați observat numărul de aplicații care pot fi puse împreună în care suntem situați, indică locuri interesante aproape, Mark rute …? În acest articol voi explica modul în care este construită o aplicație de hărți și rute cu MAPKIT.
dar ceea ce este MAPKIT? MapKit este un Apple cadru care își bazează funcționarea pe datele API și Apple Harta, astfel încât hărțile să poată fi adăugate cu ușurință la aplicațiile dezvoltate, în acest caz, pentru iOS.
un pic de swift
Acest proiect poate fi găsit complet în GitHub.
Interfață Design
Acest proiect va consta în principal dintr-o componentă MKMAPVIEW, care ne va arăta harta, pe care o vom adăuga Diferite componente în funcție de funcționalitățile pe care vrem să le adăugăm la aplicație. În plus, în acest proiect, toate acestea se vor face prin cod, fără a utiliza fișiere storyboard sau .xib.
crearea proiectului
pentru a lucra fără storyboards la stabilirea unui proiect în Xcode 11 trebuie să luăm a Puțini pași după ce au creat-o:
- haideți fișierul principal.
- În fila General, mergem la selectorul principal de interfață și ștergem principalul principal, lăsând Field Blank.
- În cele din urmă, în fila Info, vom merge la aplicarea scenei manifestare > Configurație scenă > Rolul sesiunii de aplicație IV ID = „6FDF75C273” Poziția 0 (configurarea implicită) și ștergeți câmpul Nume Storyboard.
Așa cum nu vom numi PE Main.Storyboard Pentru a începe proiectul, mergem la fișierul scenarial și în funcția scenă (scena: uiscene, sesiunea Willconnectto: uiscenession, opțiuni conexiuniOpțiuni: uiscene. Conectori) și înlocuiți conținutul său prin următorul cod:
guard let windowScene = (scene as? UIWindowScene) else { return }window = UIWindow(frame: UIScreen.main.bounds)let viewController = ViewController()window?.rootViewController = viewControllerwindow?.makeKeyAndVisible()window?.windowScene = windowScene
Adăugați o hartă la
la Adăugați o hartă pe ecran, trebuie doar să creăm o instanță de MKMAPVIEW și să o adăugați în ecranul de ecran. Pentru a face acest lucru, în clasa de vizualizare, în primul rând trebuie să importăm biblioteca Mapkit, apoi să creăm o instanță de MKMAPVIEW și să o prezentăm:
Dacă rulăm aplicația, putem vedea o hartă aproximativă pe ecranul în care suntem situați.
Pentru această hartă pentru a ne arăta exact, trebuie să folosim Clasa CLLOCACHANAGER, care se indică, vă permite să începeți și să terminați trimiterea evenimentelor de locație la aplicația noastră:
- Detectarea modificărilor în poziția utilizatorului.
- Vizualizare modificări în adresa compasului.
- Monitorizarea regiunilor de interes.
- Detectarea poziției balizelor din apropiere.
Permisiuni
Rețineți că, pentru a utiliza funcțiile de locație, înainte de a cere permisiunea Nume de utilizator. Pentru aceasta, adăugăm în fișierul info.plist, o serie de parametri (așa cum am arătat, de asemenea, utilizând notificări):
- Confidențialitate – Locație Întotdeauna și când este în uz Descriere Utilizare
- Confidențialitate – Locație Utilizarea întotdeauna Descrierea
- Confidențialitate – Locație Când este în uz Utilizare Descriere
Ce dăm ca valoare mesajul pe care dorim să-l arate utilizatorului Pentru permisiune (în acest exemplu „, permiteți accesul la locație pentru a utiliza această aplicație.”).
Odată ce fișierul info.plist, mergem Pentru a efectua verificarea aplicației, în primul rând, dacă serviciile de localizare sunt activate, iar apoi, dacă anul USUAR a acordat permisiunea și ce tip de permisiune.Ceea ce facem în primul rând este de a crea o instanță a clasei CllocationManager:
private let locationManager = CLLocationManager()
și apoi, creăm check-ul de identificare metoda, în cea pe care o vom verifica dacă serviciile de localizare sunt activate pe dispozitiv și, dacă da, vom stabili delegatul (delegatul) pentru această clasă indicat în documentație și vom indica cu ce precizie dorim să lucrăm Locație:
private func checkLocationServices() { guard CLLocationManager.locationServicesEnabled() else { // Here we must tell user how to turn on location on device return } locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyBest}
Această funcție o va numi din metoda de vizualizare. În timp ce înființim delegatul, am adoptat câteva metode ale acestui delegat care ne va permite să știm dacă schimbă permisiunea oferită de utilizator despre utilizarea locației (LocațieManager (_: DidChNGEAUTORIZARE :)) și când locația utilizatorului (LocațieManager (_: DidubateLocații :)) (facem acest lucru într-o prelungire a clasei de vizualizare pentru a avea codul organizat):
extension ViewController: CLLocationManagerDelegate { func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: ) { }}
Acum putem continua să finalizăm clasa de vizualizare prin adăugarea metodei care va arăta dacă aplicația are permisiune și ce tip de permisiune, pentru a utiliza locația. În această metodă, ceea ce facem este să numiți metoda AutorizațiStatus din clasa CllocaCheadManager și să verificați ce valoare obținută:
private func checkAuthorizationForLocation() { switch CLLocationManager.authorizationStatus() { case .authorizedWhenInUse, .authorizedAlways: mapView.showsUserLocation = true locationManager.startUpdatingLocation() break case .denied: // Here we must tell user how to turn on location on device break case .notDetermined: locationManager.requestWhenInUseAuthorization() case .restricted: // Here we must tell user that the app is not authorize to use location services break @unknown default: break }}
ca tine pot vedea, există diferite posibilități legate de autorizarea utilizării locației:
- autorizatwheninuse. Utilizatorul a autorizat aplicația pentru a începe serviciile de localizare în timp ce este în uz.
- autorizație. Utilizatorul a autorizat aplicația pentru a porni serviciile de localizare în orice moment.
- refuzat. Utilizatorul a respins utilizarea serviciilor de localizare pentru aplicație sau sunt dezactivate la nivel global în setări.
- nu a fostdeterminat. Utilizatorul nu a ales dacă aplicația poate utiliza serviciile de localizare.
- restricționat. Aplicația nu este autorizată să utilizeze serviciile de localizare.
Această funcție, verificare, o vom numi la două puncte:
- în serviciile de verificare () După stabilirea delegatului, după introducerea aplicației.
- pe metoda locației (_: DidchNGeauthorizare :), în cazul în care autorizația utilizatorului se schimbă în timpul utilizării aplicației.
private func checkLocationServices() { guard CLLocationManager.locationServicesEnabled() else { // Here we must tell user how to turn on location on device return } locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyBest checkAuthorizationForLocation()}func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { checkAuthorizationForLocation()}
Dacă rulam acum aplicația, vom vedea cum arată o alertă care solicită permisiunea de a utiliza serviciile de localizare.
Odată ce oferim permisiunea la aplicație de a utiliza serviciile de localizare, ceea ce trebuie să facem este să vă spuneți că puteți activa urmărirea poziției dispozitivului. Pentru a face acest lucru, în cadrul metodei de verificare () și în cazurile care permit utilizarea, adăugăm următorul cod:
case .authorizedWhenInUse, .authorizedAlways: mapView.showsUserLocation = true centerViewOnUser() locationManager.startUpdatingLocation() break
pe care le facem aici este să spunem că instanța lui MkmapView trebuie să afișeze poziția utilizatorului (MAPVIEW.SHOWSUSERLOCACE = TRUE), care focalizează vizualizarea pe utilizator (metoda pe care o vom crea acum) și că actualizarea locației este activată.
Afișați poziția noastră pe harta
Metoda CenterviewOnuser (), ceea ce face este determinată din locația utilizatorului, stabilirea unei regiuni dreptunghiulare centrate este un punct. Pentru aceasta folosim mkcoordlineregiune.
private let rangeInMeters: Double = 10000private func centerViewOnUser() { guard let location = locationManager.location?.coordinate else { return } let coordinateRegion = MKCoordinateRegion.init(center: location, latitudinalMeters: rangeInMeters, longitudinalMeters: rangeInMeters) mapView.setRegion(coordinateRegion, animated: true)}
Aici vă asiguram că avem poziția utilizatorului. Apoi, stabilim o regiune de 10 x 10 km axată pe utilizator. În cele din urmă, stabilim această regiune pe hartă. În acest fel, obținem următoarea imagine pe dispozitiv.
În cele din urmă, pentru a putea activa poziția utilizatorului pe hartă, în metoda LocațieManager (_: DidubdateLocații 🙂 Facem ceva de genul ceea ce am făcut pentru a centra punctul de vedere Utilizatorul:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: ) { guard let location = locations.last else { return } let coordinateRegion = MKCoordinateRegion.init(center: location.coordinate, latitudinalMeters: rangeInMeters, longitudinalMeters: rangeInMeters) mapView.setRegion(coordinateRegion, animated: true)}
Dar, în acest caz, locația pe care o primim de la ultima valoare a listei locațiilor care se întoarce Metoda.
Selectați tipul de hartă
Harta pe care o vedem în mod implicit când aplicația este activată este tipul standard. MAPKIT vă permite să afișați diferite tipuri de hărți prin modificarea valorii parametrului MapType al instanței MKMAPVIE:
- Standard. O hartă a străzilor care arată poziția tuturor drumurilor și a unor nume de drumuri.
- satelit. Imagini prin satelit ale zonei.
- hibrid. O imagine prin satelit a zonei cu informații pe drumuri și numele (într-un strat de deasupra hărții).
- satelliteflyover.O imagine prin satelit a zonei cu date din zonă (unde sunt disponibile).
- hibridflyover. O imagine hibridă prin satelit cu date din zonă (unde sunt disponibile).
- mutetandard. O hartă de stradă în care datele noastre sunt evidențiate pe detaliile hărții.
În acest caz, vom folosi doar trei tipuri de hărți: standard, satelit și hibrid.
Selecția tipului de hartă pe care vrem să o afișăm în aplicație pe care o vom folosi folosind un buton cu un meniu derulant, care poate fi descărcat ca un pachet SWIFT (FABBUTTON). Pentru aceasta, urmăm acești pași:
- din meniul Fișier XCODE > pachet swift > Adaugă Pachetul de dependență … adăugăm componenta Fababutton. URL-ul este: https://github.com/raulferrerdev/FABButton.git
- Următorul, creăm o instanță a configurației sale (icoanele utilizate sunt deja incluse în proiect):
private let mapTypeButton = FABView(buttonImage: UIImage(named: "earth")override func viewDidLoad() { super.viewDidLoad() ... configureMapTypeButton() ...} private func configureMapTypeButton() { mapTypeButton.delegate = self mapTypeButton.addSecondaryButtonWith(image: UIImage(named: "map")!, labelTitle: "Standard", action: { self.mapView.mapType = .mutedStandard }) mapTypeButton.addSecondaryButtonWith(image: UIImage(named: "satellite")!, labelTitle: "Satellite", action: { self.mapView.mapType = .satellite }) mapTypeButton.addSecondaryButtonWith(image: UIImage(named: "hybrid")!, labelTitle: "Hybrid", action: { self.mapView.mapType = .hybrid }) mapTypeButton.setFABButton()}
- După cum puteți vedea, am stabilit delegatul pentru tipul Fabview, așa că trebuie să facem Clasa Viewcontroller respectă acest protocol. Pentru că adăugăm următoarea extensie la proiect:
private func layoutUI() { ... view.addSubview(mapTypeButton) NSLayoutConstraint.activate()}
Dacă executăm aplicația, putem Verificați că putem schimba tipul hărții:
Afișare adrese
acum , ceea ce vom face este să arătăm adresa unui punct pe hartă (centrul) de pe ecran, pe care îl vom obține prin clasa Cllocation. Pentru aceasta, creăm o funcție GetCenterLocație, la care cheltuim instanța lui MkmapView pe care o avem și ne vom întoarce coordonatele punctului central:
Pentru a afla exact ce este centrul hărții, vom plasa o pictogramă în centrul hărții. Vom face acest lucru cu un element UiimageView, cu imaginea unui PIN (vom obține de la simbolurile SF de la Apple).
Pentru partea de jos a știftului exact în centrul hărții, mutăm această icoană în sus a înălțimii sale (-14,5px).
În plus, pentru a arăta adresa pe care o vom plasa a Etichetă în partea de sus a ecranului, așa cum se arată în design. Pentru aceasta, creăm o instanță a UILABEL, o configurează și stau pe ecran:
private let addressLabel = UILabel(frame: .zero)private func configureAddressLabel() { addressLabel.translatesAutoresizingMaskIntoConstraints = false addressLabel.font = .systemFont(ofSize: 18.0, weight: .medium) addressLabel.textColor = .darkText addressLabel.textAlignment = .center addressLabel.backgroundColor = .init(white: 1, alpha: 0.75) addressLabel.layer.cornerRadius = 5.0 addressLabel.clipsToBounds = true}private func layoutUI() { ... view.addSubview(addressLabel) NSLayoutConstraint.activate()}
Obținerea adresei
Pentru a obține adresa unui loc din coordonatele dvs., vom folosi clasa Clgeocoder, care, așa cum este indicat de documentația Apple, vă permite să obțineți de la lungimea și latitudinea unui punct o reprezentare „ușor de utilizat” Din acea locație:
divid id = „bc41f30811”
Clasa ClgeoDoder oferă servicii pentru conversia între o coordonată (specificată ca latitudine și longitudine) și reprezentarea prietenoasă a acestei coordonate La reprezentarea prietenoasă a coordonatei constă în mod obișnuit din informațiile despre stradă, oraș, stat și țară corespunzătoare locației date, dar poate conține, de asemenea, la un punct de interes relevant, reper sau alte informații de identificare.
Apple Documentație (clgeocoder)
Pentru a cunoaște coordonatele punctului central al hărții, vom implementa delegatul MKMAPVIEW și metoda care colectează de fiecare dată când mutați harta .
private let geoCoder = CLGeocoder()private var previousLocation: CLLocation?extension ViewController: MKMapViewDelegate { func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) { let center = getCenterLocation(for: mapView) guard let previousLocation = self.previousLocation, center.distance(from: previousLocation) > 25 else { return } self.previousLocation = center geoCoder.reverseGeocodeLocation(center) { (placemarks, error) in guard let self = self else { return } if let _ = error { // Show alert for the user return } guard let placemark = placemarks?.first else { // Show alert for the user return } let streetNumber = placemark.subThoroughfare ?? "" let streetName = placemark.thoroughfare ?? "" DispatchQueue.main.async { self.addressLabel.text = "\(streetNumber) \(streetName)" } } }}
În această metodă facem următoarele:
- în primul rând primim Coordonatele Centrului Hărții (datorită funcției GetCenterLocație pe care le-am văzut anterior).
- Pasul următor este să știți dacă există o poziție anterioară (anexă, pe care am instalat-o la Început) și, în acest caz, verificați dacă diferența în distanța până la noua poziție este superioară, în acest caz, 25 m. Dacă aceste condiții sunt îndeplinite, valoarea noilor coordonate este atribuită variabila de efectuare înapoi.
- Apoi, luăm o instanță a funcției clgeocoder și am apelat la metoda RevergeGodelococe, pe care o petrecem coordonatele centrului a ecranului.
- Această funcție returnează un bloc cu doi parametri:
typealias CLGeocodeCompletionHandler = (?, Error?) -> Void
- Din aceste două valori, verificăm că nu a apărut nicio eroare și care a returnat o poziție. Un obiect Clowermark stochează date referitoare la o anumită latitudine și longitudine (cum ar fi, de exemplu, țara, statul, orașul și direcția străzii, punctele de interes și datele legate de date din punct de vedere geografic).
- din aceste informații Suntem interesați de doi parametri: arterofare (care este adresa stradală asociată poziției indicate) și substhroughfare (care oferă informații suplimentare despre acea adresă).
- în cele din urmă , iar în firul principal, adăugăm aceste informații la etichetă.
Setați rutele
acum, ceea ce am lăsat să facem în acest proiect este de a implementa un sistem de traseu. Aceasta este, de la un punct de origine și o altă destinație, stabilește rutele optime pentru traseu.
Acest lucru poate face cu ușurință datorită MAPKIT. În acest caz, vom folosi clasa MKDIRECTIONS.Request, care ne permite să stabilim o cerere (solicitare) în care indicăm punctul de origine, destinația, tipul de transport, dacă vrem ca rute alternative să fie afișate …
- var sursa: mkmapitem?. Este punctul de plecare al rutelor.
- Var Destination: Mkmapitem?. Este destinația de dating a rutelor.
- Var Transporttype: mkdirectionstransportType. Este tipul de transport care se aplică pentru a calcula rutele. Poate fi automobil, mersul pe jos sau orice.
- var se solicită apelsalternateroutes: bool. Indică dacă vrem ca rute alternative, în cazul în care sunt disponibile.
- Var plecareTate: Data?. Este data plecării călătoriei.
- Var sosivat: data? Este data sosirii călătoriei.
ceea ce facem este să creăm o metodă care să returneze un obiect al MKDIRECTIONS.Request Tip:
func createRequest() -> MKDirections.Request? { guard let coordinate = locationManager.location?.coordinate else { return nil } let destinationCoordinate = getCenterLocation(for: mapView).coordinate let origin = MKPlacemark(coordinate: coordinate) let destination = MKPlacemark(coordinate: destinationCoordinate) let request = MKDirections.Request() request.source = MKMapItem(placemark: origin) request.destination = MKMapItem(placemark: destination) request.transportType = .automobile request.requestsAlternateRoutes = true return request}
În această metodă, primim mai întâi coordonatele punctului central al ecranului. Apoi creăm obiecte de tip MKPlacemark cu coordonatele de origine (punctul în care suntem) și destinație. În cele din urmă, creăm o instanță de tip mkdirections.Request cu indicarea originii, destinația, tipul de transport (automobile) și că dorim rute alternative.
Pentru a desena traseul, trebuie să o fac este de a începe un obiect de top Mkdirecții cu cererea (solicitarea) pe care am creat-o. Așa cum este indicat de documentația Apple, acest obiect calculează adresele și informațiile de timp de călătorie pe baza informațiilor despre traseu pe care le furnizați.
Prin urmare, vom crea o metodă de la crearea unei cereri (solicitare), obțineți a Mkdirection Type obiect și reprezintă rutele:
func drawRoutes() { guard let request = createRequest() else { return } let directions = MKDirections(request: request) directions.calculate { (response, error) in guard let response = response else { return } let routes = response.routes for route in routes { self.mapView.addOverlay(route.polyline) self.mapView.setVisibleMapRect(route.polyline.boundingMapRect, animated: true) } }}
În această metodă, odată ce obiectul a fost obținut mkdirecții, folosim FUNC Calculați metoda, care returnează un tip de obiect Mkdirecțiuni. Răspuns și o posibilă eroare:
typealias DirectionsHandler = (MKDirections.Response?, Error?) -> Void
Apoi verificăm că răspunsul este valabil Și luăm parametrii rutelor, care este o listă a obiectelor de tip Mkroute care reprezintă rutele dintre punctele sursă și punctele de destinație.
Dacă vă uitați la documentația Apple, obiectul Mkroute are un parametru numit polilinie , care conține calea traseului. Pentru a reprezenta acest aspect, ceea ce am făcut este să trecem acest parametru la metoda AddoverLay (_ Suprapunere: Mkoverlay) a obiectului MKMAPView. Apoi, schimbăm partea vizibilă a hărții utilizând metoda SetVisibleMaprecre (_ MAPRET: MKMAPRET, animat animat: bool).
În plus, trebuie să adăugăm o metodă de protocol MKMAPVIEGVIEGALEGATE, astfel încât rutele să fie trase (desenarea lor în verde și o grosime de 5px):
acum este de a adăuga un buton care ne permite să începem calculul traseului. Din designul interfeței pe care l-am arătat la început, adăugăm și configurează un element UIBUTTON, pe care îl vom adăuga ca țintă Metoda de tragere ():
private let startButton = UIButton(frame: .zero) override func viewDidLoad() { super.viewDidLoad() ... configureStartButton()}private func configureStartButton() { startButton.translatesAutoresizingMaskIntoConstraints = false startButton.setTitle("Start", for: .normal) startButton.backgroundColor = .systemRed startButton.setTitleColor(.white, for: .normal) startButton.titleLabel?.font = .systemFont(ofSize: 18.0, weight: .bold) startButton.layer.cornerRadius = 5.0 startButton.clipsToBounds = true startButton.addTarget(self, action: #selector(drawRoutes), for: .touchUpInside)}private func layoutUI() { ... view.addSubview(startButton) ... NSLayoutConstraint.activate()}extension ViewController: MKMapViewDelegate { ... func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { let renderer = MKPolylineRenderer(overlay: overlay as! MKPolyline) renderer.strokeColor = .green renderer.lineWidth = 5 return renderer }}
Concluzii
Biblioteca Mapkit Apple vă permite să vă dezvoltați într-o Mod simplu O aplicație care arată hărți, arată poziția noastră pe hartă, arată instrucțiuni de instrucțiuni dintr-un punct selectat de pe hartă și rute pentru a ajunge la acea adresă.