Adding search bar and location button to MapKit view controller - ios

This may be an easy solution but I've tried everything it seems. I cannot seem to add a search box and button to my MapKit view controller after my login window. I added my MapKit programatically rather than from the story board and can't seem to add any overlays onto it. Here is the code where I've previously tried to add UIButton and UITextField but it doesn't seem to appear so I've removed it.
I'm trying to get the user to input an address to where they'll get walking directions from their current location with a route poly overlay and also a button for finding their current location, like in the bottom corner of the normal Maps app.
import MapKit
import CoreLocation
class mapViewController: UIViewController, CLLocationManagerDelegate {
let mapView = MKMapView()
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
setupMapView()
checkLocationServices()
}
func setupMapView() {
view.addSubview(mapView)
mapView.translatesAutoresizingMaskIntoConstraints = false
mapView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
mapView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
mapView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
let region = MKCoordinateRegion.init(center: location.coordinate, latitudinalMeters: 4000, longitudinalMeters: 4000)
mapView.setRegion(region, animated: true)
}
func checkLocationAuthorization() {
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse:
mapView.showsUserLocation = true
followUserLocation()
locationManager.startUpdatingLocation()
break
case .denied:
// Show alert
break
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
case .restricted:
// Show alert
break
case .authorizedAlways:
break
}
}
func checkLocationServices() {
if CLLocationManager.locationServicesEnabled() {
setupLocationManager()
checkLocationAuthorization()
} else {
// the user didn't turn it on
}
}
func followUserLocation() {
if let location = locationManager.location?.coordinate {
let region = MKCoordinateRegion.init(center: location, latitudinalMeters: 4000, longitudinalMeters: 4000)
mapView.setRegion(region, animated: true)
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
checkLocationAuthorization()
}
func setupLocationManager() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
}
EDIT: I have managed to add a UISearchController:
let searchController: UISearchController = {
let sc = UISearchController()
sc.obscuresBackgroundDuringPresentation = false
sc.searchBar.placeholder = NSLocalizedString("Search Directions", comment: "")
sc.searchBar.barTintColor = .systemPink
return sc
}()

Related

Cannot assign locationManager() function to DispatchQueue.main.async { * }

I am getting an error while trying to assign function locationManager() in the DispatchQueue.main.async {}, ill provide whole code and specific pic of error for more clarity --> Here
I got most of the code from SeanAllen on yt since I am new to swift and learning everyday so this code isn't my logic, and the function fetchAndReloadData() is my functionalists I created to get the lat and long from the API assigning to the correct car id since it will track Vehicles on map (car tracking app)
class MapViewController: UIViewController, MKMapViewDelegate {
var globalVechicle = [Vehicles]()
var id = "6438367CC43848B497FE4604AF465D6A"
let locationManager = CLLocationManager()
let regionInMeters: Double = 10000
#IBOutlet weak var mapView: MKMapView!
#IBAction func changeMapType(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0 {
mapView.mapType = .standard
}else {
mapView.mapType = .satellite
}
}
#IBAction func closeButton(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
checkLocationServices()
}
func setupLocationManager() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
func centerViewOnUserLocation() {
if let location = locationManager.location?.coordinate {
let region = MKCoordinateRegion.init(center: location, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
mapView.setRegion(region, animated: true)
}
}
func checkLocationServices() {
if CLLocationManager.locationServicesEnabled() {
setupLocationManager()
checkLocationAuthorization()
} else {
// show alert they have to turn it on
}
}
func checkLocationAuthorization() {
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse:
mapView.showsUserLocation = true
centerViewOnUserLocation()
fetchAndReloadData()
locationManager.startUpdatingLocation()
break
case .denied:
// show alert instructing them how to turn on the permissions
break
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
case .restricted:
// Show an alert letting them know what's up
break
case .authorizedAlways:
break
#unknown default:
print("Error")
}
}
}
extension MapViewController: CLLocationManagerDelegate {
func fetchAndReloadData(){
APICaller.shared.getVehicles(for: id) {[weak self] (result) in
guard let self = self else { return }
switch result {
case .success(let vehicle):
self.globalVechicle = vehicle
DispatchQueue.main.async {
self.locationManager()
}
case .failure(let error):
print(error)
}
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let lattitude = globalVechicle[0].Latitude ,let longitude = globalVechicle[0].Longitude else { return }
let carLocation = CLLocationCoordinate2D(latitude: lattitude , longitude: longitude)
let region = MKCoordinateRegion.init(center: carLocation, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
mapView.setRegion(region, animated: true)
mapView.delegate = self
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
checkLocationAuthorization()
}
}

How can I use setregion() to set region when user location is moving only?

I have tried removing my setRegion() but it still persists to not allow map panning. The build overrides finger gestures for exploring the mapview and responds very briefly before reverting back to original position. I am not sure the issue. I have rewritten my code to a new project and there is no issue, can anyone explain to me where I am going wrong?
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
let locationManager = CLLocationManager()
let regionInMeters: Double = 15000
override func viewDidLoad() {
super.viewDidLoad()
checkLocationServices()
}
func setupLocationManager(){
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
func centerUserLocation(){
if let location = locationManager.location?.coordinate{
let region = MKCoordinateRegion.init(center: location, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
mapView.setRegion(region, animated: true)
}
}
func checkLocationServices(){
if CLLocationManager.locationServicesEnabled(){
setupLocationManager()
checkLocationAuthorization()
} else {
// alert user must turn on
}
}
func checkLocationAuthorization(){
switch CLLocationManager.authorizationStatus(){
case .authorizedWhenInUse:
mapView.showsUserLocation = true
centerUserLocation()
locationManager.startUpdatingLocation()
break
case .denied:
mapView.showsUserLocation = false
break
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
break
case .restricted:
//supervisory controls enabled
mapView.showsUserLocation = false
break
case .authorizedAlways:
mapView.showsUserLocation = true
break
}
}
}
extension ViewController: CLLocationManagerDelegate{
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else {return}
let center = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
let region = MKCoordinateRegion.init(center: center, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
mapView.setRegion(region, animated: true)
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
checkLocationAuthorization()
}
}
You are calling setRegion in didUpdateLocations - This will change the map region every time you get a location update; about once per second with best accuracy; even when the user isn’t moving
If you want user tracking on your map view, you should use the in-built userTrackingMode capabilities.

How to get nearby gas station and show direction to user with swift?

For a school project, I'm trying to get all nearby gas stations and create annotations for them and when the user click on one of these, I would like to get him the direction to go there.
this is the Maps controller that I implement by searching on internet and tutoriels:
import UIKit
import MapKit
import CoreLocation
protocol MapsControllerDelegate : class {
func mapsViewControllerDidSelectAnnotation(mapItem :MKMapItem)
}
class MapsController : UIViewController {
#IBOutlet weak var maps: MKMapView!
weak var delegate :MapsControllerDelegate!
let locationManager = CLLocationManager()
let regionInMeters: Double = 1000
override func viewDidLoad() {
super.viewDidLoad()
checkLocationServices()
}
func setupLocationManager(){
locationManager.delegate = self as! CLLocationManagerDelegate
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
func centerViewOnUserLocation(){
if let location = locationManager.location?.coordinate{
let region = MKCoordinateRegion.init(center: location, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
maps.setRegion(region, animated: true)
}
}
func checkLocationServices(){
if CLLocationManager.locationServicesEnabled(){
//setup the location manager.
setupLocationManager()
checkLocationAuthorization()
}
else{
//Show alert let the user know how to do it.
}
}
func checkLocationAuthorization(){
switch CLLocationManager.authorizationStatus(){
case .authorizedWhenInUse:
maps.showsUserLocation = true
centerViewOnUserLocation()
locationManager.startUpdatingLocation()
case .denied:
break
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
case .restricted:
break
case .authorizedAlways:
break
}
}
func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
let annotationView = views.first!
if let annotation = annotationView.annotation {
if annotation is MKUserLocation {
centerViewOnUserLocation()
populateNearByPlaces()
}
}
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
let annotation = view.annotation as! PlaceAnnotation
self.delegate.mapsViewControllerDidSelectAnnotation(mapItem: annotation.mapItem)
}
func populateNearByPlaces(){
print("Im heeeeeerrrrreeeeeee")
if let location = locationManager.location?.coordinate{
let region = MKCoordinateRegion.init(center: location, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = "Gas Station"
request.region = region
let search = MKLocalSearch(request: request)
search.start { (response, error) in
guard let response = response else {
return
}
for item in response.mapItems {
print("I'm here")
print(item)
let annotation = PlaceAnnotation()
annotation.coordinate = item.placemark.coordinate
annotation.title = item.name
annotation.mapItem = item
DispatchQueue.main.async {
self.maps.addAnnotation(annotation)
}
}
}
}
}
}
extension MapsController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else {return}
let center = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
let region = MKCoordinateRegion.init(center: center, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
maps.setRegion(region, animated: true)
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
checkLocationAuthorization()
}
}
}
I don't have any errors or the app stops working but I'm not getting the results expected. In the Maps scene I'm just getting the user's actual location on the map and that's it.

How to show location on map view on swift? I believe my code is current but simulator doesn't show location or the blue dot?

I am trying to show on my current location and a blue dot on the map using the map view on swift. However it does not show my location nor the blue dot I'm very positive of my code but I can't get it to show! it's probably an issue with the settings?
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
let locationManager = CLLocationManager()
let regionInMeters: Double = 1000
override func viewDidLoad() {
super.viewDidLoad()
checkLocationServices()
}
func setupLocationManager() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
func centerViewOnUserLocation() {
if let location = locationManager.location?.coordinate {
let region = MKCoordinateRegion.init(center: location, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
mapView.setRegion(region, animated: true)
}
}
func checkLocationServices() {
if CLLocationManager.locationServicesEnabled() {
setupLocationManager()
checkLocationAuthorrization()
} else {
// Show alert letting the user know they have to turn this on.
}
}
func checkLocationAuthorrization() {
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse:
mapView.showsUserLocation = true
centerViewOnUserLocation()
locationManager.startUpdatingLocation()
break
case .denied:
// Show alret instructing them how to turn on permissions
break
case .notDetermined:
break
case .restricted:
// Show alret letting them know what's up
break
case .authorizedAlways:
break
}
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
let center = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
let region = MKCoordinateRegion.init(center: center, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
mapView.setRegion(region, animated: true)
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
checkLocationAuthorrization()
}
}
You have to add below permission in Info.plist file
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Usage Description</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Usage Description</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Usage Description</string>
Import libraries:
import MapKit
import CoreLocation
Set delegates:
CLLocationManagerDelegate,MKMapViewDelegate
Add variable:
private var locationManager: CLLocationManager!
private var currentLocation: CLLocation?
write below code on viewDidLoad():
mapView.delegate = self
mapView.showsUserLocation = true
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
// Check for Location Services
if CLLocationManager.locationServicesEnabled() {
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
Write delegate method for location:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
defer { currentLocation = locations.last }
if currentLocation == nil {
// Zoom to user location
if let userLocation = locations.last {
let viewRegion = MKCoordinateRegion(center: userLocation.coordinate, latitudinalMeters: 2000, longitudinalMeters: 2000)
mapView.setRegion(viewRegion, animated: false)
}
}
}
Thats all, Now you able to see your current location and blue dot.
To show user location on map do following steps:
try this path-
Go to product->Edit Scheme->Options->select Allow Location simulation and select default location.
Here You can also add custom location using GPX file.
After setting clean and run the app.
The problem seems to be in method checkLocationAuthorrization, here you have to ask for locationManager.requestWhenInUseAuthorization() when the status is notDetermined, like so:
func checkLocationAuthorization(authorizationStatus: CLAuthorizationStatus? = nil) {
switch (authorizationStatus ?? CLLocationManager.authorizationStatus()) {
case .authorizedAlways, .authorizedWhenInUse:
locationManager.startUpdatingLocation()
mapView.showsUserLocation = true
case .restricted, .denied:
// show alert instructing how to turn on permissions
print("Location Servies: Denied / Restricted")
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
}
}
Also change the delegate method to pass the current status received
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
self.checkLocationAuthorization(authorizationStatus: status)
}
Also note that locationManager.requestWhenInUseAuthorization() will not work, if Info.plist does not have following usage description(s), so edit the Info.plist file and make sure:
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Message for AlwaysAndWhenInUseUsageDescription</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Message for AlwaysUsageDescription</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Message for WhenInUseUsageDescription</string>
Finally, you got to wait for an location update to call centerViewOnUserLocation, like so
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
let span = MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
let region = MKCoordinateRegion(center: location.coordinate, span: span)
mapView.setRegion(region, animated: true)
}
Simulator doesn't show current location but you can manually add a location in it by:
Debug -> Location -> Custom Location and then give coordinates.
In your viewDidLoad method please write the below code.
Default value of showsUserLocation is false. So, we have to update it's default value.
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.showsUserLocation = true
checkLocationServices()
}
Refer my updated code.
import UIKit
import MapKit
class ViewController: UIViewController {
#IBOutlet weak var mapview: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
let location = CLLocationCoordinate2D(latitude: "", longitude: "")
let span = MKCoordinateSpanMake(0.5, 0.5)
let region = MKCoordinateRegion(center: location, span: span)
mapview.setRegion(region, animated: true)
let annotation = MKPointAnnotation()
annotation.coordinate = location
annotation.title = "your title"
mapview.addAnnotation(annotation)
}

Can't get User Location Using MKMapViewDelegate

I have been reading tutorials about getting user location for several hours, yet I cannot seem to get it. I have my ViewController class implementing CLLocationManagerDelegate and MKMapViewDelegate.
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.requestWhenInUseAuthorization()
self.mapView.delegate = self
self.mapView.showsUserLocation = true
print("Is User Location Visible: \(self.mapView.userLocation.location?.coordinate.latitude)")
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
print("Authorization Status = \(CLLocationManager.authorizationStatus() == .Denied || CLLocationManager.authorizationStatus() == CLAuthorizationStatus.NotDetermined)")
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
locationManager.delegate = self
}
else{
print("location services disabled")
}
}
This seems like it would work, as I have set showsUserLocation to true with MKMapView and started updating location services with CLLocationManager.
However, the printout of viewDidLoad() is:
Is User Location Visible: nil
Authorization Status = false
Whenever I try to access user location, it's nil.
I have NSLocationWhenInUseUsageDescription set in Info.plist as well, it successfully asked me if I want to allow location services and I clicked yes.
Thanks
EDIT:
Here is the full code of MapViewController.swift:
import UIKit
import MapKit
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
var mapView: MKMapView!
let locationManager = CLLocationManager()
override func loadView() {
// create a map view
mapView = MKMapView()
// set the map view we just created as the view for this controller
view = mapView
let ​​​​​​​​​​​​​​​​segmentedControl = UISegmentedControl(items: ["Standard", "Hybrid", "Satellite", "My Location"])
​​​​​​​​​​​​​​​​segmentedControl.backgroundColor = UIColor.whiteColor().colorWithAlphaComponent(0.5)
​​​​​​​​​​​​​​​​segmentedControl.selectedSegmentIndex = 0
​​​​​​​​​​​​​​​​segmentedControl.addTarget(self, action: #selector(MapViewController.mapTypeChanged(_:)), forControlEvents: .ValueChanged)
​​​​​​​​​​​​​​​​segmentedControl.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(​​​​​​​​​​​​​​​​segmentedControl)
let topConstraint = ​​​​​​​​​​​​​​​​segmentedControl.topAnchor.constraintEqualToAnchor(topLayoutGuide.bottomAnchor, constant: 8)
let margins = view.layoutMarginsGuide
let leadingConstraint = ​​​​​​​​​​​​​​​​segmentedControl.leadingAnchor.constraintEqualToAnchor(margins.leadingAnchor)
let trailingConstraint = ​​​​​​​​​​​​​​​​segmentedControl.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor)
topConstraint.active = true
leadingConstraint.active = true
trailingConstraint.active = true
}
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.requestWhenInUseAuthorization()
mapView.delegate = self
mapView.showsUserLocation = true
print("Is User Location Visible: \(mapView.userLocation.location?.coordinate.latitude)")
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
print("Authorization Status = \(CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse)")
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
locationManager.delegate = self
}
else{
print("location services disabled")
}
}
func mapTypeChanged(segControl: UISegmentedControl) {
switch segControl.selectedSegmentIndex {
case 0:
mapView.mapType = .Standard
case 1:
mapView.mapType = .Hybrid
case 2:
mapView.mapType = .Satellite
case 3:
zoomOnLocation()
default:
break
}
}
//Zoom to last known location
func zoomOnLocation() {
print("locations = \(self.mapView.userLocation.coordinate.longitude) ")
var location:CLLocationCoordinate2D = CLLocationCoordinate2DMake(self.mapView.userLocation.coordinate.latitude, self.mapView.userLocation.coordinate.longitude)
var region:MKCoordinateRegion = MKCoordinateRegionMake(location, MKCoordinateSpanMake(0.5, 0.5))
print("zooming")
mapView.setRegion(region, animated: true)
}
//Delegate if Location has Changed
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
var locValue:CLLocationCoordinate2D = manager.location!.coordinate
print("locations = \(locValue.latitude) \(locValue.longitude)")
}
//Delegate if Authorization Status has Changed
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus){
locationManager.startUpdatingLocation()
}
func mapView(mapView: MKMapView!, didUpdateUserLocation userLocation: MKUserLocation!){
let coord = userLocation.coordinate
print("delegate: coord = \(coord.longitude)")
}
func mapViewDidFinishLoadingMap(mapView: MKMapView!){
print("finished loading mapview")
self.mapView.showsUserLocation = true
print("Is User Location Visible: \(self.mapView.userLocationVisible)")
}
func mapView(mapView: MKMapView!, mapViewWillStartLocatingUser userLocation: MKUserLocation!){
let coord = userLocation.coordinate
print("delegate: coord = \(coord.longitude)")
}
}

Resources