Pensando em Swift

Você notou o número de aplicativos que podem ser montados em que estamos localizados, indicam lugares interessantes, Mark rotas …? Neste artigo, explicarei como uma aplicação de mapas e rotas é construída com o Mapkit.

Mas o que é mapkit? O Mapkit é uma estrutura de estrutura que baseia sua operação nos dados da API e da Apple Map, para que os mapas possam ser facilmente adicionados aos aplicativos desenvolvidos, neste caso, para iOS.

um pouco de swift

Este projeto pode ser encontrado completo no GitHub.

Design de interface

Este projeto consistirá basicamente de um componente MkmapView, que nos mostrará o mapa, que adicionaremos componentes diferentes de acordo com as funcionalidades que queremos adicionar ao aplicativo. Além disso, neste projeto, tudo isso será feito através de código, sem usar storyboards ou arquivos .xib.

criação do projeto

para trabalhar sem storyboard ao estabelecer um projeto no Xcode 11, temos que tomar um Poucos passos depois de tê-lo criado:

  • Vamos remover o arquivo main.storyboard.
  • Na guia Geral, vamos para o seletor de interface principal e excluindo o principal, deixando o campo em branco.
você tem que excluir o principal e deixar o campo Em branco.

  • Finalmente, na guia Informações, estamos indo para a cena do aplicativo manifesto > cena configuração > Papel de sessão de aplicativos > Item 0 (configuração padrão) e exclua o campo Nome do Storyboard.
Apaga do campo Nome do storyboard no arquivo info.plist.

Como agora não vamos chamar o principal.Storyboard para iniciar o projeto, vamos para o arquivo scenedelegate.swift e na função de cena (_ cena: Uiscene, WillConnectTo Session: Uiscenesession, conexões de opções: Uiscene. ConnectionOptions) e substituir seu conteúdo pelo 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

Adicionar um mapa ao nosso

para Adicione um mapa na tela, apenas temos que criar uma instância de mkmapview e adicioná-lo à exibição da tela. Para fazer isso, na classe ViewController, em primeiro lugar, temos que importar a biblioteca MAPKIT, criamos uma instância de mkmapview e apresentá-lo:

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 executarmos o aplicativo, podemos ver um mapa aproximado na tela de onde estamos localizados.

apresentação do mapa.

Para este mapa para nos mostrar exatamente, devemos usar O cllocolocationManager, que como Apple indica, permite que você inicie e termine o envio de eventos de localização para o nosso aplicativo:

  • detectar alterações na posição do usuário.
  • Visualizar alterações No endereço da bússola.
  • Monitorar regiões de interesse.
  • detectar a posição de beacons próximos.

permissões

Tenha em mente que, a fim de usar as funções de localização, antes de ter que pedir permissão Nome de usuário. Para isso, nós adicionamos no arquivo info.plist, uma série de parâmetros (como também mostrou para usar notificações):

  • privacidade – local sempre e quando em uso Descrição de uso
  • privacidade – localização sempre descrição de uso
  • privacidade – localização quando em uso Descrição de uso

o que damos como valor a mensagem que queremos mostrar ao usuário Para permissão (neste exemplo, permitir acesso de localização para usar este aplicativo. ‘).

modificação do arquivo info.plist para a solicitação de permissão.

Uma vez que o arquivo info.Plist, vamos Para fazer a verificação do aplicativo, primeiro, se os serviços de localização estiverem ativados e, se o ano USUAR tiver dado permissão e que tipo de permissão.O que fazemos em primeiro lugar é criar uma instância da classe CLLocationManager:

private let locationManager = CLLocationManager()

e, em seguida, criamos o checklocationservice método, naquele, verificaremos se os serviços de localização estão habilitados no dispositivo e, se assim for, estabeleceremos o delegado (delegado) para esta classe, conforme indicado na documentação e indicaremos com que precisão queremos trabalhar Localização:

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 função ligará-las do método ViewDidLoad. Enquanto estabelecemos o delegado, adotamos alguns métodos desse delegado que nos permitirá saber se ele muda a permissão fornecida pelo usuário sobre o uso do local (locationManager (_: didchangeautilização :)) e quando a localização do usuário (locationManager (_: didupdatelocations :)) (Fazemos isso em uma extensão da classe 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 continuar a completar a classe ViewController adicionando o método que procurará se o aplicativo tiver permissão e que tipo de permissão usar o local. Nesse método, o que fazemos é chamar o método de autorizaçãoStatus da classe CLLocationManager e verificar qual valor obteremos:

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 }}

Como você Pode ver, existem diferentes possibilidades em relação à autorização do uso do local:

  • autorizadowheninuse. O usuário autorizou o aplicativo para iniciar serviços de localização enquanto estiver em uso.
  • autorizados. O usuário autorizou o aplicativo para iniciar os serviços de localização a qualquer momento.
  • negado. O usuário rejeitou o uso de serviços de localização para o aplicativo ou está desabilitado globalmente em configurações.
  • NotDeterminado. O usuário não foi escolhido se o aplicativo puder usar os serviços de localização.
  • restrito. A aplicação não está autorizada a usar os serviços de localização.

Esta função, marcação de verificação, nós chamá-lo em dois pontos:

  • nos serviços de checklocations () Depois de estabelecer o delegado, depois de entrar no aplicativo.
  • no método do locationManager (_: didchangeautilização :), caso a autorização do usuário seja alterada durante o uso do aplicativo.
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 executarmos o aplicativo, veremos como ele mostra um alerta pedindo permissão para usar os serviços de localização.

.

Uma vez que damos permissão ao aplicativo para usar os serviços de localização, o que temos que fazer é dizer que você pode ativar o rastreamento da posição do dispositivo. Para fazer isso, dentro do método de checkauthorationforingLocation () e nos casos que permitem o uso, adicionamos o seguinte código:

case .authorizedWhenInUse, .authorizedAlways: mapView.showsUserLocation = true centerViewOnUser() locationManager.startUpdatingLocation() break

lo Que estamos fazendo aqui é dizer que a instância do mkmapview tem que mostrar a posição do usuário (mapview.showsuserlocation = true), que focaliza a exibição no usuário (método que criaremos agora) e que a atualização do local é ativada.

Mostrar nossa posição no mapa

O método centerviewonuser (), o que ele faz é determinar a partir do local do usuário, estabelecendo uma região retangular centrada é um ponto. Para isso, usamos mkcoordinatereregion.

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)}

Aqui, primeiro nos certificamos de que temos a posição do usuário. Então, estabelecemos uma região de 10 x 10 km focadas no usuário. Finalmente, estabelecemos esta região no mapa. Desta forma, obtemos a seguinte imagem no dispositivo.

Finalmente, para poder ativar a posição do usuário no mapa, no método LocationManager (_: didupdatelocations 🙂 Fazemos algo como o que fizemos para centrar a vista O usuário:

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)}

Mas neste caso, o local a obtemos do último valor da lista de locais que retorna O método.

Selecione o tipo de mapa

O mapa que vemos por padrão quando o aplicativo é ligado é o tipo padrão. O Mapkit permite que você exiba diferentes tipos de mapas alterando o valor do parâmetro MapType da instância MKMAPVIEW:

  • padrão. Um mapa de ruas que mostra a posição de todas as estradas e alguns nomes de estradas.
  • satélite. Imagens de satélite da área.
  • híbrido. Uma imagem de satélite da área com informações sobre as estradas e o nome (em uma camada acima do mapa).
  • satelliteflyover.Uma imagem de satélite da área com dados da área (onde estão disponíveis).
  • hybridflyover. Uma imagem de satélite híbrida com dados da área (onde eles estão disponíveis).
  • mutetandard. Um mapa de rua onde nossos dados são destacados nos detalhes do mapa.

Neste caso, só usaremos três tipos de mapas: padrão, satélite e híbrido.

A seleção do tipo de mapa que queremos mostrar no aplicativo, vamos usá-lo usando um botão com um menu suspenso, que pode ser baixado como um pacote rápido (Fabbutton). Para isso, seguimos estas etapas:

  • do menu do arquivo Xcode > Pacote Swift > Adicionar Pacote DependEcy … Adicionamos o componente Fabbutton. O URL é: https://github.com/raulferrerdev/FABButton.git
  • Em seguida, criamos uma instância do e e sua configuração (os ícones usados já estão incluídos no projeto):
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 você pode ver, nós estabelecemos o delegado para o tipo Fabview, para que devemos fazer o ViewController de classe em conformidade com este protocolo. Para isso adicionamos a seguinte extensão ao projeto:
  • Finalmente, no método Layoutui Adicionamos o botão à visualização e indico sua postagem:
private func layoutUI() { ... view.addSubview(mapTypeButton) NSLayoutConstraint.activate()}

Se executarmos o aplicativo, podemos Verifique se podemos mudar de mapa Tipo:

Mostrar endereços

agora , o que vamos fazer é mostrar o endereço de um ponto no mapa (o centro) na tela, que obteremos através da classe de clllocation. Para isso, criamos uma função GetCenterLocation, para as quais passamos a instância do MKMAPVIEW que temos e nos devolveremos as coordenadas do ponto central:

func getCenterLocation(for mapView: MKMapView) -> CLLocation { let coordinates = mapView.centerCoordinate return CLLocation(latitude: coordinates.latitude, longitude: coordinates.longitude)}

saber exatamente o que é o centro do mapa, vamos colocar um ícone no centro do mapa. Faremos isso com um elemento uiimageview, com a imagem de um pino (vamos obter dos símbolos SF da Apple).

private let pointer = UIImageView(image: UIImage(systemName: "mappin"))private func layoutUI() { ... pointer.translatesAutoresizingMaskIntoConstraints = false pointer.tintColor = .red ... view.addSubview(pointer) NSLayoutConstraint.activate()}

Para a parte inferior do pino exatamente no centro do mapa, movemos este ícone acima da metade de sua altura (-14.5px).

Além disso, para mostrar o endereço que colocaremos um etiqueta na parte superior da tela, conforme mostrado no design. Para isso, criamos uma instância de UILabel, nós configuramos e estamos na tela:

obtendo o endereço

Para obter o endereço de um lugar de suas coordenadas, usaremos a classe ClgeoCoder, que, conforme indicado pela documentação da Apple, permite obter a partir do comprimento e latitude de um ponto uma representação ‘amigável’ A partir desse local:

A classe Clgeododer fornece serviços para conversão entre uma coordenada (especificada como uma latitude e longitude) e a representação amigável dessa coordenada A representação amigável da coordenada normalmente consiste na informação da rua, da cidade, do estado e do país correspondente ao local dado, mas também pode conter no ponto de interesse, no ponto de referência ou de outras informações de identificação.

Apple Documentação (Clgocoder)

Para saber as coordenadas do ponto central do mapa, implementaremos o Delegado MkmapView e o método que recolhe a cada vez que você mover 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 desse método nós fazemos o seguinte:

  • primeiro de tudo o que recebemos As coordenadas do centro do mapa (graças à função GetCenterLocation, que vimos anteriormente).
  • O próximo passo é saber se há uma posição anterior (anteriormente, que instanciamos no Começo) e, nesse caso, verifique se a diferença à distância para a nova posição é superior, neste caso, 25 m. Se estas condições forem cumpridas, o valor das novas coordenadas é atribuído à variável de elocação anterior.
  • Então, tomamos uma instância da função Clgocoder e chamamos o método de reversegeocodelocation, que passamos as coordenadas do centro da tela.
  • Esta função retorna um bloco com dois parâmetros:
typealias CLGeocodeCompletionHandler = (?, Error?) -> Void

  • desses dois valores, verificamos que nenhum erro ocorreu e que retornou uma posição. Um objeto ClplACemark armazena dados referentes a determinada latitude e longitude (como, por exemplo, o país, o estado, a cidade e a direção da rua, pontos de interesse e dados geograficamente relacionados).
  • dessas informações estamos interessados em dois parâmetros: trânsito (que é o endereço de rua associado à posição indicada) e subthoroughfare (que fornece informações adicionais sobre esse endereço).
  • finalmente , e no segmento principal, adicionamos esta informação ao rótulo.

Definir rotas

Agora, o que deixamos para fazer neste projeto é implementar um sistema de rota. Ou seja, de um ponto de origem e outro destino, estabeleça as rotas ótimas para a rota.

Isso pode fazer isso facilmente graças ao Mapkit. Neste caso, usaremos a classe Mkdirections.Request, que nos permite estabelecer uma solicitação (solicitação) na qual indicamos o ponto de origem, o destino, o tipo de transporte, se quisermos que as rotas alternativas sejam mostradas …

  • var fonte: mkmapitem?. É o ponto de partida das rotas.
  • Var destino: mkmapitem?. É o destino de namoro das rotas.
  • var> transporttype: mkdirectiontransporttype. É o tipo de transporte que se aplica a calcular as rotas. Pode ser automóvel, caminhada, trânsito ou qualquer.
  • VAR Squitedsalternaterts: bool. Indica se queremos rotas alternativas, caso estejam disponíveis.
  • var de ponto de embaramento: Data. É a data de partida da viagem.
  • Var ChegadaDate: Data? É a data de chegada da viagem.

O que fazemos é criar um método que retornará um objeto das mkdirections.request Type:

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}

Neste método, primeiro obtemos as coordenadas do ponto central da tela. Em seguida, criamos objetos tipo mkplacemark com as coordenadas de origem (o ponto em que estamos) e destino. Finalmente, criamos uma instância de tipo mkdirections.Request com a origem, o destino, o tipo de transporte (automóvel) e que queremos rotas alternativas.

Para desenhar a rota, tenho que fazer isso é iniciar um objeto Top Mkdirections com a solicitação (solicitação) que criamos. Como indicado pela documentação da Apple, esse objeto calcula endereços e informações de tempo de viagem com base nas informações de rota que você fornece.

Portanto, vamos criar um método que a partir da criação de uma solicitação (solicitação), obtenha um Objeto de tipo de mkdirection e representam as rotas:

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, uma vez que o objeto foi obtido mkdirections, usamos a func Calcular método, que retorna um tipo de objeto mkdirections. Resposta e um possível erro:

Então vamos verificar se a resposta é válida e nós levamos os parâmetros de rotas, que é uma lista de objetos do tipo mkroute que representam as rotas entre os pontos de origem e de destino.

Se você olhar para a documentação da Apple, o objeto MKRoute tem um parâmetro chamado Polyline , que contém o caminho da rota. Para representar esse layout, o que fizemos é passar por este parâmetro para o método ADDOVERLAY (_ Overlay: MKOverLay) do objeto MkmapView. Em seguida, alteramos a parte visível do mapa usando o método SETVISIBLEMAPRECT (_ MAPRET: mkmaprect, animado animado: bool).

Além disso, devemos adicionar um método do protocolo MkMapViewDelegate para que as rotas forem desenhadas (desenhos-os em cor verde e uma espessura 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 precisamos é adicionar um botão que nos permite iniciar o cálculo da rota. Do design da interface, mostramos no começo, adicionamos e configuramos um elemento UIBUTTON, que adicionaremos como alvo o método DrawRouts ():

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 }}

conclusões

a biblioteca MapKit da Apple permite que você se desenvolva em um Simples Way Um aplicativo que mostra mapas, mostre nossa posição no mapa, mostre-nos instruções de um ponto selecionado no mapa e rotas para atingir esse endereço.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *