Hai notato il numero di applicazioni che possono essere messe insieme in cui ci troviamo, indicare luoghi interessanti vicino, Mark itinerari …? In questo articolo spiegherò come un’applicazione di mappe e percorsi è costruita con MAPKIT.
Ma cosa è Mapkit? MapKit è una mela quadro che basa la sua operazione sui dati della mappa API e Apple, in modo che le mappe possano essere facilmente aggiunte alle applicazioni sviluppate, in questo caso, per iOS.
un po ‘di swift
Questo progetto può essere trovato completo a GitHub.
Design dell’interfaccia
Questo progetto consisterà fondamentalmente da un componente MKMAPView, che ci mostrerà la mappa, che aggiungeremo Componenti diversi in base alle funzionalità che vogliamo aggiungere all’applicazione. Inoltre, in questo progetto, tutto ciò verrà fatto tramite il codice, senza utilizzare storyboard o file .xib.
Creazione del progetto
per lavorare senza storyboard Quando si stabilisce un progetto in Xcode 11 dobbiamo prendere un Pochi passaggi dopo averlo creato:
- Rimuovi il file principale.Storyboard.
- Nella scheda Generale, andiamo al selettore principale dell’interfaccia ed eliminiamo il principale, lasciando il campo vuoto.
- Infine, nella scheda Informazioni, stiamo andando a domanda scena manifest > Configurazione della scena > Ruolo della sessione dell’applicazione > Elemento 0 (configurazione predefinita) ed eliminare il campo Nome dello Storyboard.
Come ora non chiameremo il main.Storyboard per avviare il progetto, andiamo al file SCenedelegate.Swift, e nella funzione scena (_ scena: Uiscene, willconnectto sessione: uiscenession, opzioni connectionOptions: Uiscene. ConnectionOptions) e sostituire il suo contenuto dal seguente codice:
guard let windowScene = (scene as? UIWindowScene) else { return }window = UIWindow(frame: UIScreen.main.bounds)let viewController = ViewController()window?.rootViewController = viewControllerwindow?.makeKeyAndVisible()window?.windowScene = windowScene
Aggiungi una mappa al nostro
a Aggiungi una mappa sullo schermo, dobbiamo solo creare un’istanza di MKMapView e aggiungerla alla visualizzazione dello schermo. Per fare ciò, nella classe ViewController, prima di tutto dobbiamo importare la Biblioteca Mapkit, quindi creiamo un’istanza di MKMapView e la presenteremo:
import UIKitimport MapKitclass ViewController: UIViewController { private let mapView = MKMapView(frame: .zero) override func viewDidLoad() { super.viewDidLoad() layoutUI() } private func layoutUI() { mapView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(mapView) NSLayoutConstraint.activate() }}
Se eseguiamo l’applicazione possiamo vedere una mappa approssimativa sullo schermo di dove ci troviamo.
Per questa mappa per mostrarci esattamente, dobbiamo usare esattamente La classe CllocationManager, che come Apple indica, consente di avviare e terminare l’invio di eventi di posizione alla nostra applicazione:
- rilevare le modifiche alla posizione dell’utente.
- Visualizza le modifiche Nell’indirizzo della bussola.
- monitor regioni di interesse.
- rileva la posizione dei beacon vicini.
Autorizzazioni
Tieni presente che per utilizzare le funzioni di localizzazione, prima di dover chiedere il permesso Nome utente. Per questo, aggiungeremo il file Info.plist, una serie di parametri (come ho anche dimostrato di utilizzare le notifiche):
- Privacy – Posizione sempre e quando in uso DESCRIZIONE DESCRIZIONE
- Privacy – Posizione Utilizzo sempre DESCRIZIONE
- Privacy – Posizione quando si utilizza l’uso DESCRIZIONE
Cosa diamo come valore Il messaggio che vogliamo mostrare all’utente di chiedere Per il permesso (in questo esempio, “Consenti accesso alla posizione per utilizzare questa app”).
Una volta il file info.plist, stiamo andando Per effettuare il controllo dell’applicazione, in primo luogo, se i servizi di localizzazione sono abilitati, e quindi, se l’anno USUAR ha dato il permesso e quale tipo di autorizzazione.Quello che facciamo in primo luogo è quello di creare un’istanza della classe ClLoCationManager:
private let locationManager = CLLocationManager()
e poi, creiamo il checklocationservice Metodo, in quello controlleremo se i servizi di localizzazione sono abilitati sul dispositivo e, in caso affermativo, stabiliremo il delegato (Delegato) per questa classe come indicato nella documentazione e indicheremo con quale precisione vogliamo lavorare Posizione:
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}
Questa funzione lo chiamerà dal metodo ViewDidLoad. Mentre stabiliamo il delegato, abbiamo adottato un paio di metodi di questo delegato che ci consentiremo di sapere se cambia l’autorizzazione fornita dall’utente sull’utilizzo della posizione (LocationManager (_: DidCageAuthrization :)) e quando la posizione dell’utente (LocationManager (_: DiduPdatelocazioni :)) (Lo facciamo in un’estensione della classe ViewController per avere il codice organizzato):
extension ViewController: CLLocationManagerDelegate { func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: ) { }}
Ora possiamo continuare a completare la classe ViewController aggiungendo il metodo che guarderà se l’applicazione ha l’autorizzazione e quale tipo di autorizzazione, utilizzare la posizione. In questo metodo ciò che facciamo è chiamare il metodo AuthorizationStatus della classe ClLoCationManager e controlla quale valore otteniamo:
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 }}
come te Può vedere, ci sono diverse possibilità relative all’autorizzazione dell’uso della posizione:
- autorizzato wheninuse. L’utente ha autorizzato l’applicazione per avviare i servizi di localizzazione mentre è in uso.
- autorizzato. L’utente ha autorizzato l’applicazione per avviare i servizi di localizzazione in qualsiasi momento.
- negato. L’utente ha rifiutato l’uso dei servizi di localizzazione per l’applicazione o sono disabilitati a livello globale in Impostazioni.
- notdeterminato. L’utente non ha scelto se l’applicazione può utilizzare i servizi di localizzazione.
- limitato. L’applicazione non è autorizzata a utilizzare i servizi di localizzazione.
Questa funzione, checkauthorizationforlocation, lo chiameremo a due punti:
- nei servizi di controllo () Dopo aver stabilito il delegato, dopo aver inserito l’applicazione.
- sul metodo LocalizzazioneManager (_: DEDCangeAuthrization :), nel caso in cui l’autorizzazione dell’utente cambia durante l’uso dell’applicazione.
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()}
Se ora eseguiamo l’applicazione, vedremo come mostra un avviso che chiederà il permesso di utilizzare i servizi di localizzazione.
Una volta che offriamo il permesso all’applicazione per utilizzare i servizi di localizzazione, ciò che dobbiamo fare è dirti che è possibile attivare il tracciamento della posizione del dispositivo. Per fare ciò, all’interno del metodo checkauthorizationforlocation () e nei casi che consentono l’uso, aggiungiamo il seguente codice:
case .authorizedWhenInUse, .authorizedAlways: mapView.showsUserLocation = true centerViewOnUser() locationManager.startUpdatingLocation() break
lo Che stiamo facendo qui è dire che l’istanza di MKMAPView deve mostrare la posizione dell’utente (MapView.ShowsuserLocation = true), che focalizza la vista sull’utente (metodo che creeremo ora) e che l’aggiornamento della posizione è attivato.
Mostra la nostra posizione sulla mappa
Il metodo Centerviewonuser (), ciò che fa è determinare dalla posizione dell’utente, stabilire una regione rettangolare centrata è un punto. Per questo utilizziamo MkoorDinateregion.
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)}
Qui, per prima cosa, assicurati che abbiamo la posizione dell’utente. Quindi, stabiliamo una regione di 10 x 10 km focalizzata sull’utente. Infine, stabiliamo questa regione sulla mappa. In questo modo, otteniamo la seguente immagine sul dispositivo.
Infine, per essere in grado di attivare la posizione dell’utente sulla mappa, nel metodo LocalizzatoreManager (_: DiduPdatelocazioni 🙂 Facciamo qualcosa come quello che abbiamo fatto per centrare la vista in L’utente:
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)}
Ma in questo caso, la posizione che lo otteniamo dall’ultimo valore dell’elenco delle posizioni che restituiscono Il metodo.
Selezionare il tipo di mappa
La mappa Vediamo per impostazione predefinita quando l’applicazione è attivata è il tipo standard. MapKit consente di visualizzare diversi tipi di mappe modificando il valore del parametro MaplePe dell’istanza MKMAPView:
- standard. Una mappa delle strade che mostrano la posizione di tutte le strade e alcuni nomi stradali.
- satellite. Immagini satellitari dell’area.
- ibrido. Un’immagine satellitare dell’area con informazioni sulle strade e il nome (in un livello sopra la mappa).
- satelliteflyover.Un’immagine satellitare dell’area con dati dall’area (dove sono disponibili).
- hybridflyover. Un’immagine satellitare ibrida con dati dall’area (dove sono disponibili).
- mutandard. Una mappa stradale in cui i nostri dati sono evidenziati sui dettagli della mappa.
In questo caso, utilizzeremo solo tre tipi di mappe: standard, satellite e ibrido.
La selezione del tipo di mappa che vogliamo mostrare nell’applicazione lo useremo utilizzando un pulsante con un menu a discesa, che può essere scaricato come pacchetto Swift (Fabbutton). Per questo, seguiamo questi passaggi:
- dal menu del file Xcode > pacchetto Swift > aggiungi Pacchetto Dipendice … Aggiungiamo il componente Fabbutton. L’URL è: https://github.com/raulferrerdev/FABButton.git
- Avanti, creiamo un’istanza della configurazione e della sua configurazione (le icone utilizzate sono già incluse nel progetto):
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()}
- Come puoi vedere, abbiamo stabilito il delegato per il tipo Fabview, quindi dobbiamo fare il La classe ViewController è conforme al presente protocollo. Per questo aggiungeremo la seguente estensione al progetto:
- Infine, nel metodo Layoutui Aggiungiamo il pulsante alla vista e indiciamo il tuo post:
private func layoutUI() { ... view.addSubview(mapTypeButton) NSLayoutConstraint.activate()}
Se eseguiamo l’applicazione, possiamo Verificare che possiamo cambiare il tipo di mappa:
Visualizza gli indirizzi
Ora , quello che faremo è mostrare l’indirizzo di un punto sulla mappa (il centro) sullo schermo, che otterremo attraverso la classe di cllocazione. Per questo creiamo una funzione GetCenterLocation, a cui spendiamo l’istanza di MkMapView che abbiamo e ci restituiamo le coordinate del punto centrale:
func getCenterLocation(for mapView: MKMapView) -> CLLocation { let coordinates = mapView.centerCoordinate return CLLocation(latitude: coordinates.latitude, longitude: coordinates.longitude)}
Per sapere esattamente qual è il centro della mappa, metteremo un’icona nel centro della mappa. Lo faremo con un elemento UiimageView, con l’immagine di un PIN (otterremo dai simboli SF di Apple).
private let pointer = UIImageView(image: UIImage(systemName: "mappin"))private func layoutUI() { ... pointer.translatesAutoresizingMaskIntoConstraints = false pointer.tintColor = .red ... view.addSubview(pointer) NSLayoutConstraint.activate()}
Per il fondo del perno esattamente nel centro della mappa, spostiamo questa icona la metà della sua altezza (-14.5px).
Inoltre, per mostrare l’indirizzo che portiamo a Etichetta nella parte superiore dello schermo, come mostrato nel design. Per questo, creiamo un’istanza di Uilabel, lo configuramo e stacciamo sullo schermo:
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()}
ottenendo l’indirizzo
Per ottenere l’indirizzo di un luogo dalle tue coordinate, useremo la classe clgeatoder, che indicata dalla documentazione Apple, consente di ottenere dalla lunghezza e dalla latitudine di un punto una rappresentazione ‘user-friendly’ Da quella posizione:
La classe CLGLEODODER fornisce servizi per la conversione tra una coordinata (specificata come latitudine e longitudine) e la rappresentazione user-friendly di quella coordinata Alla rappresentazione user-friendly della coordinata consiste in genere della via, della città, dello stato e delle informazioni sul paese corrispondenti alla posizione data, ma può anche contenere il punto di interesse, il punto di riferimento o altre informazioni identificative.
Apple Documentazione (clGeocoder)
Per conoscere le coordinate del punto centrale della mappa, implementeremo il delegato MKMAPView e il metodo che raccoglie ogni volta che si sposta la mappa .
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)" } } }}All’interno di questo metodo facciamo quanto segue:
- Prima di tutto otteniamo Le coordinate del Centro della mappa (grazie alla funzione GetCenterLocation, che abbiamo visto in precedenza).
- Il prossimo passo è sapere se c’è una posizione precedente (precedentelocazione precedente, che abbiamo istanziato al Inizio) E, in tal caso, verificare che la differenza nella distanza della nuova posizione sia superiore, in questo caso, 25 m. Se queste condizioni sono soddisfatte, il valore delle nuove coordinate è assegnato alla variabile di precedente linea.
- Quindi, prendiamo un’istanza della funzione clonoocoder e chiamiamo il metodo del recerseGodelocation, che trascorriamo le coordinate del centro dello schermo.
- Questa funzione restituisce un blocco con due parametri:
typealias CLGeocodeCompletionHandler = (?, Error?) -> Void
- Di questi due valori, controlliamo che nessun errore si è verificato e che ha restituito una posizione. Un oggetto ClpPacemark memorizza i dati che si riferiscono a determinati latitudine e longitudine (come, ad esempio, il paese, lo stato, la città e la direzione della strada, punti di interesse e dati geograficamente correlati).
- di queste informazioni siamo interessati a due parametri: Borrouse (che è l’indirizzo di strada associato alla posizione indicata) e alla subtoroughfare (che fornisce ulteriori informazioni su quell’indirizzo).
- Infine E nel thread principale, aggiungiamo queste informazioni all’etichetta.
Impostare percorsi
Ora, ciò che abbiamo lasciato a fare in questo progetto è implementare un sistema di percorso. Cioè, da un punto di origine e da un altro di destinazione, stabilire le rotte ottimali per il percorso.
Può farlo facilmente grazie a Mapkit. In questo caso useremo la classe mkdirections.request, che ci consente di stabilire una richiesta (richiesta) in cui indichiamo il punto di origine, la destinazione, il tipo di trasporto, se vogliamo che i percorsi alternativi vengano mostrati …
- var sorgente: mkmapitem?. È il punto di partenza dei percorsi.
- Var Destinazione: mkmapitem?. È la destinazione di appuntamenti dei percorsi.
- var TransportType: mkdirectionstransporttype. È il tipo di trasporto che si applica per calcolare i percorsi. Può essere automobile, camminare, transito o qualsiasi.
- var readsalternateriutes: bool. Indica se vogliamo rotte alternative, nel caso in cui siano disponibili.
- Var parturetato: data?. È la data di partenza del viaggio.
- var arrivoDate: data? È la data di arrivo del viaggio.
Cosa facciamo è creare un metodo che restituirà un oggetto del mkdirections.request Tipo:
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}In questo metodo, per prima cosa otteniamo le coordinate del punto centrale dello schermo. Quindi creiamo oggetti di tipo mkplacemark con le coordinate di origine (il punto in cui siamo) e la destinazione. Infine, creiamo un’istanza di tipo mkdirections.request con indicare l’origine, la destinazione, il tipo di trasporto (automobile) e che vogliamo percorsi alternativi.
Per disegnare il percorso, devo farlo è quello di avviare un oggetto mkdirections top con la richiesta (richiesta) che abbiamo creato. Come indicato dalla documentazione Apple, questo oggetto calcola gli indirizzi e le informazioni sul tempo di viaggio in base alle informazioni del percorso fornite.
Pertanto, creeremo un metodo dalla creazione di una richiesta (richiesta), ottenere un Oggetto tipo mkdirection e rappresentano i percorsi:
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) } }}In questo metodo, una volta che l’oggetto è stato ottenuto mkdirections, usiamo la func Calcola il metodo, che restituisce un tipo di oggetto Mkdirections. Risposta e un possibile errore:
typealias DirectionsHandler = (MKDirections.Response?, Error?) -> VoidQuindi controlliamo che la risposta sia valida e prendiamo i parametri percorsi, che è un elenco di oggetti di tipo Mkroute che rappresentano i percorsi tra i punti di origine e di destinazione.
Se si guarda la documentazione Apple, l’oggetto MKROUTE ha un parametro chiamato polilinea , che contiene il percorso del percorso. Per rappresentare questo layout, ciò che abbiamo fatto è passare questo parametro al metodo AddOverlay (_ Overlay: MKoverlay) dell’oggetto MKMAPView. Quindi cambiamo la parte visibile della mappa utilizzando il metodo SetVisibleMaprect (_ Mapret: MKMaprect, animato animato: BOOL).
Inoltre, dobbiamo aggiungere un metodo del protocollo MKMAPViewDelegate in modo che i percorsi siano disegnati (disegnandoli in colore verde e uno spessore di 5 px):
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { let renderer = MKPolylineRenderer(overlay: overlay as! MKPolyline) renderer.strokeColor = .green renderer.lineWidth = 5 return renderer}Ora, ciò di cui abbiamo bisogno è aggiungere un pulsante Ci consente di avviare il calcolo del percorso. Dal design dell’interfaccia abbiamo mostrato all’inizio, aggiungiamo e configuraremo un elemento di uociazione, che aggiungeremo come target il metodo di vigilanza ():
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 }}conclusioni
La biblioteca Mapkit Apple consente di sviluppare in a Semplice modo Un’applicazione che mostra mappe, mostra la nostra posizione sulla mappa, mostrare le indicazioni da un punto selezionato sulla mappa e percorsi per raggiungere quell’indirizzo.