Notou o número de aplicacións que se poden colocar en que estamos situados, indican lugares interesantes próximos, marca Rutas …? Neste artigo vou explicar como unha aplicación de mapas e rutas está construída con MapKit.
Pero que é MapKit? MapKit é un cadro de mazá que basea a súa operación nos datos do mapa API e Apple, polo que os mapas poden ser facilmente engadidos ás aplicacións desenvolvidas, neste caso, para iOS
un pouco de Swift
Este proxecto pode atoparse completo en github.
Deseño de interface
Este proxecto basicamente consistirá nun compoñente MkMapview, que nos mostrará o mapa, que engadiremos Diferentes compoñentes segundo as funcionalidades que queremos engadir á aplicación. Ademais, neste proxecto, todo isto farase a través do código, sen usar storyboards ou ficheiros .xib.
Creación do proxecto
para traballar sen storyboards ao establecer un proxecto no Xcode 11 temos que tomar un Poucos pasos despois de crerlo:
- Imos eliminar o ficheiro principal.
- Na pestana Xeral, imos ao selector de interface principal e elimina o principal, deixando o campo en branco.
- Finalmente, na páxina de información, imos a manifestar a escena da aplicación > Configuración da escena > Role Sesión de aplicación > Tema 0 (configuración predeterminada) e borrar o campo Nome do storyboard.
Como agora non chamaremos ao Main.StoryBoard para iniciar o proxecto, imos ao esceneDelegate.Swift Ficheiro e, na función de escena (_ Escena: Uiscene, WillConnectto Sesión: Uislenesession, Opcións ConnectionStions: Uiscene. ConnectionOptions) e substituír o seu contido mediante o seguinte código:
guard let windowScene = (scene as? UIWindowScene) else { return }window = UIWindow(frame: UIScreen.main.bounds)let viewController = ViewController()window?.rootViewController = viewControllerwindow?.makeKeyAndVisible()window?.windowScene = windowScene
Engadir un mapa ao noso
a Engade un mapa na pantalla, só temos que crear unha instancia de mkmapview e engadila á vista da pantalla. Para iso, na clase ViewController, en primeiro lugar, temos que importar a biblioteca MapKit, entón nós creamos unha instancia de Mkmapview e presente:
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 executamos a aplicación podemos ver un mapa aproximado na pantalla de onde estamos situados.
Para que este mapa nos mostre exactamente, debemos usar O cllocationmanager de clase, que como indica Apple, permítelle comezar e rematar o envío de eventos de localización á nosa aplicación:
- detectar cambios na posición do usuario.
- Ver cambios na dirección compás.
- Monitorizar rexións de interese.
- Detectar a posición de balizas próximas
Permisos
Teña en conta que, a fin de usar as funcións de localización, antes de que teñamos que pedir permiso Nome de usuario. Para iso, engadimos no ficheiro info.plist, unha serie de parámetros (como tamén mostrei para usar notificacións):
- Privacidade – Localización sempre e cando se usan a descrición do uso
- Privacidade – Localización Sempre Uso Descrición
- Privacidade – Localización Cando use a descrición do uso
O que damos como valor a mensaxe que queremos mostrar ao usuario a preguntar Para o permiso (neste exemplo, “Permitir o acceso a localización para usar esta aplicación. ‘).
Unha vez que o ficheiro info.plist, imos Para facer a comprobación da aplicación, primeiro, se os servizos de localización están habilitados, e despois, se o ano Usuar deu permiso e que tipo de permiso.O que facemos, en primeiro lugar, é crear unha instancia da clase CLLocationManager:
private let locationManager = CLLocationManager()
e despois, creamos a comprobación de verificación método, no que comprobaremos se os servizos de localización están habilitados no dispositivo e, se é así, estableceremos o delegado (delegado) desta clase indicado na documentación e indicaremos con que precisión queremos traballar Localización:
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}
Esta función chamará desde o método de visualización. Mentres establecemos o delegado, adoptamos un par de métodos deste delegado que nos permitirán saber se cambia o permiso dado polo usuario sobre o uso da localización (LocationManager (_: Didchangeutorización :)) e cando a localización do usuario (LocationMarger (_: Didupdatelocations :)) (Facemos isto nunha extensión da clase ViewController para ter o código organizado):
extension ViewController: CLLocationManagerDelegate { func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: ) { }}
Agora podemos seguir a completar a clase ViewController engadindo o método que verá se a aplicación ten permiso e que tipo de permiso, para usar a localización. Neste método o que facemos é chamar ao método de autorizaciónStatus da clase CLLocationManager e comprobar que valor obtemos: “7bd7f7a5d5”>
como ti Pódese ver, hai diferentes posibilidades sobre a autorización do uso da localización:
- autorizadoWhenUseuse. O usuario autorizou a aplicación para iniciar os servizos de localización mentres está en uso.
- Autorización. O usuario autorizou a aplicación para iniciar os servizos de localización en calquera momento.
- negado. O usuario rexeitou o uso de servizos de localización para a aplicación ou está desactivada globalmente en Configuración.
- notdeterminado. O usuario non escolleu se a aplicación pode usar servizos de localización.
- restrinxido. A aplicación non está autorizada a usar os servizos de localización.
Esta función, CheckAuthorizationForLocation, chamarémolo en dous puntos:
- nos servizos de verificación () Despois de establecer o delegado, despois de introducir a aplicación.
- no método de localización (_: DIDCHANGEAutorización :), no caso de que a autorización do usuario cambie durante o uso da aplicación.
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 agora executamos a aplicación, veremos como mostra unha alerta que solicita permiso para usar os servizos de localización.
Unha vez que damos permiso á aplicación para usar os servizos de localización, o que temos que facer é dicirlle que pode activar o seguimento da posición do dispositivo. Para iso, dentro do método CheckAuthorizationForLocation () e dentro dos casos que permiten o uso, engadimos o seguinte código:
case .authorizedWhenInUse, .authorizedAlways: mapView.showsUserLocation = true centerViewOnUser() locationManager.startUpdatingLocation() break
lo que estamos a facer aquí é dicir que a instancia de Mkmapview ten que mostrar a posición do usuario (MAPVIEW.howsUserLocation = True), que enfoca a vista sobre o usuario (método que imos crear agora) e que a actualización de localización está activada.
Amosar a nosa posición no mapa
O método centerviewonuser (), o que fai é determinar a partir da localización do usuario, establecendo unha rexión rectangular centrada é un punto. Para iso, usamos mkcoordineregion.
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)}
Aquí, primeiro asegurámosnos / asegurámonos de ter a posición do usuario. Entón, establecemos unha rexión de 10 x 10 km enfocada ao usuario. Finalmente, establecemos esta rexión no mapa. Deste xeito, obtemos a seguinte imaxe no dispositivo.
Finalmente, para poder activar a posición do usuario no mapa, no método localManager (_: DIDUPDAlocations 🙂 Facemos algo así como o que fixemos para centrar a vista en o usuario:
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)}
Pero neste caso, a localización que obtemos do último valor da lista de localizacións que devolve O método.
Seleccione o tipo de mapa
O mapa que vemos por defecto cando a aplicación está activada é o tipo estándar. MapKit permítelle mostrar diferentes tipos de mapas cambiando o valor do parámetro MAPTYPE da instancia MKMAPVIEW:
- estándar. Un mapa de rúas que mostra a posición de todas as estradas e algúns nomes de estradas.
- Satélite. Imaxes por satélite da zona.
- híbrido. Unha imaxe de satélite da área con información sobre as estradas e o nome (nunha capa superior ao mapa).
- SatelladoFlyOver.Unha imaxe de satélite da área con datos da zona (onde están dispoñibles).
- hybridflyover. Unha imaxe de satélite híbrida con datos da zona (onde están dispoñibles).
- MutetAndard. Un mapa de rúa onde se destacan os nosos datos nos detalles do mapa.
Neste caso, só usaremos tres tipos de mapas: estándar, satélite e híbrido.
A selección do tipo de mapa que queremos mostrar na aplicación que o utilizaremos usando un botón cun menú despregable, que se pode descargar como paquete SWIFT (FABBUTTON). Para iso, seguimos estes pasos:
- do menú de ficheiros Xcode > paquete Swift > Paquete dependente … Engadimos o compoñente fabbutton. A URL é: https://github.com/raulferrerdev/FABButton.git
- A continuación, creamos unha instancia da e a súa configuración (as iconas empregadas xa están incluídas no proxecto):
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()}
- Como podes ver, establecemos o delegado do tipo FABVIEW, polo que debemos facer o Clase ViewController cumpre con este protocolo. Por iso engadimos a seguinte extensión ao proxecto:
- Finalmente, no método LAYOUTUI engadimos o botón á vista e indique a súa publicación:
private func layoutUI() { ... view.addSubview(mapTypeButton) NSLayoutConstraint.activate()}
Se executamos a aplicación, podemos Comprobe que podemos cambiar de tipo de mapa:
Amosar enderezos
agora , o que imos facer é mostrar a dirección dun punto no mapa (o centro) na pantalla, que imos obter a través da clase de cllocation. Para iso, creamos unha función de GetcenterLocation, á que pasamos a instancia de Mkmapview que temos e imos devolvernos as coordenadas do punto central:
func getCenterLocation(for mapView: MKMapView) -> CLLocation { let coordinates = mapView.centerCoordinate return CLLocation(latitude: coordinates.latitude, longitude: coordinates.longitude)}
Para saber exactamente cal é o centro do mapa, faremos unha icona no centro do mapa. Faremos isto cun elemento UiimageView, coa imaxe dun PIN (obteremos dos símbolos SF de Apple).
Para a parte inferior do PIN exactamente no centro do mapa, movemos esta icona á metade da súa altura (-14.5px).
Ademais, para mostrar a dirección que faremos un Etiqueta na parte superior da pantalla, como se mostra no deseño. Para iso, creamos unha instancia de Uilabel, configurámola e quedamos na pantalla:
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()}
Obtención da dirección
Para obter a dirección dun lugar das súas coordenadas, utilizaremos a clase CLGNOOCODER, que tal e como se indica a documentación de Apple, permítelle obter a partir da lonxitude e latitude dun punto unha representación ‘Usuario-Friendly’ A partir dese lugar:
A clase clgelododer ofrece servizos para a conversión entre unha coordenada (especificada como latitude e lonxitude) e a representación amigable da coordenada A representación amigable da coordenada normalmente consiste na información da rúa, cidade, estatal e do país correspondente ao lugar indicado, pero tamén pode conter ao punto de interese relevante, o marco ou outra información de identificación.
Apple Documentación (CLGIOOCODER)
Para coñecer as coordenadas do punto central do mapa, implementaremos o delegado MKMAPVIEW e o método que recolle cada vez que mova o mapa .
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)" } } }}Dentro deste método facemos o seguinte:
- Primeiro de todo que obtemos as coordenadas do centro do mapa (grazas á función de GetcenterLocation, que vimos anteriormente).
- O seguinte paso é saber se hai unha posición previa (previa loita, que temos instantado no Inicio) e, nese caso, comprobe que a diferenza na distancia á nova posición é superior, neste caso, 25 m. Se se cumpren estas condicións, o valor das novas coordenadas está asignado á variable de anteriormente.
- Entón, tomamos unha instancia da función de clgóococoder e chama o método de reversooCodelocation, que pasamos as coordenadas do centro da pantalla.
- Esta función devolve un bloque con dous parámetros:
typealias CLGeocodeCompletionHandler = (?, Error?) -> Void
- destes dous valores, comprobamos que non se produciu ningún erro e que devolveu unha posición. Un obxecto de Clplacemark almacena datos referíndose a certa latitude e lonxitude (como, por exemplo, o país, o Estado, a cidade ea dirección da rúa, puntos de interese e datos xeográficamente relacionados).
- desta información estamos interesados en dous parámetros: a vía (que é a dirección de rúa asociada á posición indicada) e subtoroufar (que proporciona información adicional sobre ese enderezo).
- Finalmente , e no fío principal, engadimos esta información á etiqueta.
Establecer rutas
Agora, o que deixamos de facer neste proxecto é implementar un sistema de rutas. É dicir, desde un punto de orixe e outro de destino, establece as rutas óptimas para a ruta.
Isto pode facelo facilmente grazas a mapkit. Neste caso, imos usar a clase MKDirections.Request, que nos permite establecer unha solicitude (solicitude) na que indicamos o punto de orixe, o destino, o tipo de transporte, se queremos que as rutas alternativas se mostren …
- var up: mkmapitem?. É o punto de partida das rutas.
- var destino: mkmapitem?. É o destino de mozo das rutas.
- var transportType: mkdirectionstransporttype. É o tipo de transporte que se aplica para calcular as rutas. Pode ser automóbil, camiñar, tránsito ou calquera.
- Var solicitudsalternatatorOutes: bool. Indica se queremos rutas alternativas, no caso de que estean dispoñibles.
- Var Departuretate: Data?. É a data de saída da viaxe.
- Var ArrivalDate: Data? É a data de chegada da viaxe.
O que facemos é crear un método que devolverá un obxecto do MKDirections.Request Type:
Neste método, primeiro obtemos as coordenadas do punto central da pantalla. A continuación, creamos obxectos tipo MKPlacemark coas coordenadas de orixe (o punto onde estamos) e destino. Finalmente, creamos unha instancia de tipo mkdirections.Request con indicar a orixe, o destino, o tipo de transporte (automóbil) e que queremos rutas alternativas.
Para debuxar a ruta, teño que facelo é comezar un obxecto de MKDirections superior coa solicitude (solicitude) que creamos. Como indica a documentación de Apple, este obxecto calcula enderezos e información de tempo de viaxe en función da información da ruta que proporciona.
Polo tanto, crearemos un método que desde a creación dunha solicitude (solicitude) obtivese un Obxecto tipo mkdirection e representa as rutas:
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) } }}Neste método, unha vez que se obtivo o obxecto MKDirections, usamos o func Calcular o método, que devolve un tipo de obxecto MKDirections. Resposta e un posible erro:
typealias DirectionsHandler = (MKDirections.Response?, Error?) -> VoidEntón comprobamos que a resposta é válida E tomamos os parámetros de rutas, que é unha lista de obxectos de tipo Mkroute que representan as rutas entre os puntos de orixe e destino.
Se mira a documentación de Apple, o obxecto Mkroute ten un parámetro chamado Polyline , que contén o camiño da ruta. Para representar este esquema, o que fixemos é pasar este parámetro ao método de Addoverlay (_ Overlay: MkOverlay) do obxecto MkMapview. Entón cambiamos a parte visible do mapa usando o método setvisiblemaprect (_ Mapret: mkmaprect, animado animado: bool).
Ademais, debemos engadir un método do protocolo MkMapviewDelegate para que as rutas sexan deseñadas (debuxalos en cor verde e un espesor de 5px):
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { let renderer = MKPolylineRenderer(overlay: overlay as! MKPolyline) renderer.strokeColor = .green renderer.lineWidth = 5 return renderer}Agora, o que necesitamos é engadir un botón que permítenos iniciar o cálculo da ruta. Desde o deseño da interface que mostramos ao comezo, engadimos e configura un elemento UiButton, que engadiremos como obxectivo o método de procura ():
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 }}Conclusións
A biblioteca de mapkit de Apple permítelle desenvolver nun Simple Way unha aplicación que mostra mapas, amosar a nosa posición no mapa, amosar indicacións dun punto seleccionado no mapa e rutas para chegar a ese enderezo.