CLLocationManager delegate not called ios 11 (swift) - ios

Hey guys I'm new to iOS programming and I have been struggling to figure this out. My locationManager delegate is not being called. Here is my code:
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 it as *the* view of this view controller
view = mapView
}
override func viewDidLoad() {
super.viewDidLoad()
print("MapViewController loaded its view.");
mapView.showsUserLocation = true;
let btnImage = UIImage(named: "mapview-track-user.png");
//Tracking button creation code
let trackButton = UIButton(type: UIButtonType.custom) as UIButton;
trackButton.frame = CGRect(x: 10, y: 500, width: 60, height: 60);
trackButton.setImage(btnImage, for: UIControlState.normal);
trackButton.addTarget(self, action: #selector(centerMapOnUserButtonClicked), for: .touchDown);
//Shadow for track user location button
trackButton.layer.shadowColor = UIColor.lightGray.cgColor;
trackButton.layer.shadowOffset = CGSize(width: 0.0, height: 2.0);
trackButton.layer.masksToBounds = false;
trackButton.layer.shadowRadius = 1.0;
trackButton.layer.shadowOpacity = 0.9;
mapView.addSubview(trackButton);
locationManager.delegate = self;
mapView.delegate = self;
locationManager.requestAlwaysAuthorization();
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
if CLLocationManager.locationServicesEnabled() {
print("Location servicese are enabled!");
locationManager.startUpdatingLocation();
locationManager.startUpdatingHeading();
}
else {
print("Location Services not endabled!");
}
}
//Called upon clicking track user button in order to snap map to user
#objc func centerMapOnUserButtonClicked() {
self.mapView.setUserTrackingMode(MKUserTrackingMode.followWithHeading, animated: true);
print("Track button clicked!");
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [AnyObject]!) {
let location = locations.last as! CLLocation
let center = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
var region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))
region.center = mapView.userLocation.coordinate;
mapView.setRegion(region, animated: true)
print("*** locationManager Called! ***");
}
}
I am trying to follow various guides online and get familiar with tracking users and eventually displaying other users on the map. Right now I am also slightly confused as to the uses of the CLLocationManager delegate vs. mapView.setUserTrackingMode when it comes to tracking a user in real time.
I have searched all around for a solution but have been completely stumped here. Any help is greatly appreciated.

The map view call will produce temporary permission to location. Whereas the location manager will allow you to specify behavior more rigorously. Please be sure to add the appropriate line to your info.plist. In your case; add the middle entry from below and your location manager should start updating.

Related

Why my app crashes when loading a MapKit View in disconnected from computer iPhone?

I have a UIViewController with a MapKit View inside of it, the app runs perfectly in simulator and physical device connected to my mac, but when I try to run the app with the device disconnected, it crashes when going to the MapKit View
I Tried to make the MapKit View the initial View Controller
I tried to reset my phone
I tried to reset Xcode
import UIKit
import ChameleonFramework
class PanicMapViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = FlatYellowDark()
configureNavigationBar()
}
func configureNavigationBar() {
navigationController?.navigationBar.barTintColor = FlatYellowDark()
navigationController?.navigationBar.barStyle = .black
navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor:ContrastColorOf(FlatYellowDark(),returnFlat: true)]
navigationItem.title = "Side Menu"
}
}
I expect that when my physical device is disconnected I can run the app and see the MapKit view.
I was having this problem (Xcode 11 building for iOS 12.4) and stumbled across this question whilst looking for a solution.
For me, I resolved the problem by adding 'Maps' as a 'Capability' under the 'Signing and Capabilities' tab of the project file.
You need to
import MapKit
Inside PanicMapViewController
I was having same issue. even after typing import MapKit.
i did some research and found out the device was not able to find current location.
import UIKit
import MapKit
Class PSSearchVC: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet var mapview: MKMapView!
let locationmanager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
mapview.mapType = MKMapType.standard
let location = CLLocationCoordinate2DMake(22.4651, 70.0771)
let span = MKCoordinateSpan.init(latitudeDelta: 0.01, longitudeDelta:
0.01)
let coordinate = CLLocationCoordinate2D.init(latitude: 21.282778, longitude: -157.829444) // provide you lat and long
let region = MKCoordinateRegion.init(center: coordinate, span: span)
mapview.setRegion(region, animated: true)
let annonation = MKPointAnnotation()
annonation.coordinate = location
annonation.title = "Chandi Bazar"
annonation.subtitle = "Jamnagar"
//
mapview.addAnnotation(annonation)
self.locationmanager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled()
{
locationmanager.delegate = self
locationmanager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationmanager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
print("locations = \(locValue.latitude) \(locValue.longitude)")
locationmanager.stopUpdatingLocation()
}
}

Google maps change mapType on segmented control click

Here is the problem. In my view controller, I have placed a segmented control on the top.
On controller load it displays the map, circle and the marker. Now, when I click the segmented control to change the map type it goes into the code block on select but never updates the Map type.
Please see the code here.
class MapController: UIViewController, CLLocationManagerDelegate , GMSMapViewDelegate {
var locationManager:CLLocationManager!
let marker = GMSMarker()
var roundCircle: GMSCircle!
var mapView = GMSMapView()
private var didPerformGeocode = false
enum maptype:NSInteger
{
case standardmap = 0
case satellitemap = 1
}
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func segmentedControlAction(_ sender: UISegmentedControl)
{
switch sender.selectedSegmentIndex {
case maptype.standardmap.rawValue:
mapView.mapType = .normal
**no change in the map type**
case maptype.satellitemap.rawValue:
mapView.mapType = .satellite
**no change in the map type**
default:
break
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
determineMyCurrentLocation()
}
func determineMyCurrentLocation() {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0] as CLLocation
guard !didPerformGeocode else { return }
didPerformGeocode = true
locationManager.stopUpdatingLocation()
let camera = GMSCameraPosition.camera(withLatitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude, zoom: 13.0)
let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
mapView.delegate = self
self.view = mapView
let markerImage = UIImage(named: "mapMarker")!.withRenderingMode(.alwaysTemplate)
let markerView = UIImageView(image: markerImage)
markerView.tintColor = UIColor.red
marker.iconView = markerView
marker.position = CLLocationCoordinate2D(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude)
marker.map = mapView
roundCircle = GMSCircle(position: camera.target, radius: 1500) //0.96 Miles
roundCircle.fillColor = UIColor.red.withAlphaComponent(0.3)
roundCircle.strokeColor = nil
roundCircle.map = mapView
}
}
Honestly, I don't use Google Maps
However, to change MapTypes in Apple Maps I use
#IBAction func Whatever(_ sender: UISegmentedControl) {
mapView.mapType = MKMapType.init(rawValue: UInt(sender.selectedSegmentIndex)) ?? .normal
}
Maybe it will work

Could not inset legal attribution from corner 4 swift

I am new to Xcode and mobile app. I am doing an app to find the current location. I tested it on the simulator and got this message in the console.
"Could not inset legal attribution from corner 4". What does it mean and how can I fix it?
import UIKit
import Alamofire
import AlamofireImage
import MapKit
import CoreLocation
class MapVC: UIViewController
#IBOutlet weak var mapView: MKMapView!
var locationManager = CLLocationManager()
let authorizationStatus = CLLocationManager.authorizationStatus()
let regionRadius: Double = 1000
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
locationManager.delegate = self
configureLocationServices()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func centerMapPressed(_ sender: Any) {
if authorizationStatus == .authorizedAlways || authorizationStatus == .authorizedWhenInUse{
centerMapOnUserLocation()
}
}
MKMapViewDelegate:
func centerMapOnUserLocation(){
guard let coordinate = locationManager.location?.coordinate else{return}
let coordinateRegion = MKCoordinateRegionMakeWithDistance(coordinate, regionRadius*2.0, regionRadius * 2.0 )
mapView.setRegion(coordinateRegion, animated: true)
}
CLLocationManagerDelegate:
func configureLocationServices(){
if authorizationStatus == .notDetermined{
locationManager.requestAlwaysAuthorization()
}else{
return}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
centerMapOnUserLocation()
}
this issue occurs when the required information in the right form is not found. if your device i trying to get some location or some number a variable, and that a variable is required for afun.. the value is not set into a, but afun is called that this error occurs..
call your cell's coordinates in viewdidload without any other function.
Here is the simplest way to get your cell's position.
1. you should have updated privacies of the devices under infor.plist
Add following lines into your plist before closing tag
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Privacy - Location Always and When In Use Usage Description</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Privacy - Location Always Usage Description</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Privacy - Location When In Use Usage Description</string>
after that.. this is the simplest working code .. i'm happily using it with no issue..
import CoreLocation
import MapKit
class AddressVc: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var map: MKMapView!
var locationManager = CLLocationManager()
here is code to move map in center for the required location
let latDelta:Double = 0.5
let lngDelta:Double = 0.5
let latitude:Double = 37.57554038
let longitude:Double = -122.40068475
let locationcoordinates = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let zoomSpan = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lngDelta)
let region = MKCoordinateRegion(center: locationcoordinates, span: zoomSpan)
self.map.setRegion(region, animated: true)
in the above code.. if you put device's lat and lng postions in latitude and lognitude that will move the map to your's devices's location.
now how to get device's location.
here is the code you can put into your viewDidLoad() function.
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
// device latitude = (locationManager.location?.coordinate.latitude)!
// device longitude = (locationManager.location?.coordinate.longitude)!
}
this is detect the device's latitude and longitude values. you can put them in above mentioned code..
Here is the full code.
import CoreLocation
import MapKit
class AddressVc: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var map: MKMapView!
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
devicelatitude = (locationManager.location?.coordinate.latitude)!
devicelongitude = (locationManager.location?.coordinate.longitude)!
let latDelta:Double = 0.5
let lngDelta:Double = 0.5
let latitude:Double = devicelatitude
let longitude:Double = devicelongitude
let locationcoordinates = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let zoomSpan = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lngDelta)
let region = MKCoordinateRegion(center: locationcoordinates, span: zoomSpan)
self.map.setRegion(region, animated: true)
}
I hope this will help you.. if not.. there would be many other who are facing this issue.
==========================
With the simulator open, go to the DEBUG menu on the mac top bar, the last option is location, mine was showing this error when set to NONE. Set it Custom Location, first with you prompt you to a custom location on a new window, fill that, close the simulator, and relaunch the app. It should get the custom location from your code now. (andrecasarini credit)
This error occurs when the "Legal" link on the bottom left of the MapView is not within bounds or is obscured. There are similar errors for the map's scale and for the compass that appears when rotating.
Consider using size constraints or a safeAreaInset.
I believe the legal inset bug is in Apple's mapkit initialization code and unrelated to what we do when we use the code. Here's my reasoning.
If you use storyboards and the UIMapKit drag and drop module, the legal inset error pops up somewhere between the call to the initial call to application in the app delegate and the first viewDidLoad call, i.e., it's an OS error.
I got curious if I could work around it and wrote a version that doesn't use the storyboard editor. I was thinking that perhaps the editor was inserting some broken code into the app. My test app has two views, the start view and a second view with the map on it. The code is shown below. Don't be harsh, it's my first attempt at understanding what's going on with controllers views and subviews using programmatic views and subviews. It isn't pretty but it works to isolate the legal inset bug.
I started by building a new xcode project and deleting the storyboard. I then replaced the application function stub in appdelegate with the following code:
func application(_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
self.window = UIWindow(frame: UIScreen.main.bounds)
let viewController = ViewController()
self.window?.rootViewController = viewController
self.window?.makeKeyAndVisible()
let mapView = mapViewController()
mapView.otherView = viewController
viewController.mapView = mapView
return true
}
Even though the mapView is created here, the legal inset error doesn't show up. What does show up are a couple of other messages about scale and compass having some sort of issue. Since I was focusing on the legal inset bug, I ignored them and moved on.
I then replaced the default view controller with code that created two subviews, one a subview that serves as a button to switch to the second view controller and the second subview that serves as "you're on the first view" marker view. That required figuring out how to handle a tap. The color changing code in the tap handler was initial "hello world. I see a tap" code and serves no other purpose.
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController,CLLocationManagerDelegate {
let DynamicView = UIView()
let switchView = UIView(frame: CGRect(x: UIScreen.main.bounds.minX+10, y: UIScreen.main.bounds.minY+20, width: 40, height: 40))
public var mapView:mapViewController? = nil
public var otherView:UIViewController? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = .white
DynamicView.backgroundColor = .yellow
DynamicView.layer.cornerRadius = 25
DynamicView.layer.borderWidth = 2
switchView.backgroundColor = .orange
switchView.layer.borderWidth = 2
var gR = UITapGestureRecognizer(target: self, action:#selector(self.handlebigTap(_:)))
DynamicView.addGestureRecognizer(gR)
gR = UITapGestureRecognizer(target: self, action:#selector(self.handlesmallTap(_:)))
switchView.addGestureRecognizer(gR)
self.view.addSubview(switchView)
self.view.addSubview(DynamicView)
DynamicView.translatesAutoresizingMaskIntoConstraints = false
let margins = view.layoutMarginsGuide
DynamicView.leadingAnchor.constraint(equalTo: margins.leadingAnchor, constant: 10).isActive = true
DynamicView.trailingAnchor.constraint(equalTo: margins.trailingAnchor, constant: 10).isActive = true
DynamicView.topAnchor.constraint(equalTo: margins.topAnchor, constant: 40).isActive = true
DynamicView.bottomAnchor.constraint(equalTo: margins.topAnchor, constant: 110).isActive = true
}
#objc func handlebigTap(_ sender: UITapGestureRecognizer) {
if ( self.DynamicView.backgroundColor == .green){
self.DynamicView.backgroundColor = .blue
} else {
self.DynamicView.backgroundColor = .green
}
}
#objc func handlesmallTap(_ sender: UITapGestureRecognizer) {
if ( self.switchView.backgroundColor == .orange){
self.switchView.backgroundColor = .blue
present(mapView!, animated: true, completion: nil)
} else {
self.switchView.backgroundColor = .orange
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
At this point, I checked to see if the first call to viewDidLoad triggered the legal inset error and saw that it did not. That meant the error was being triggered somewhere in the mapKit initialization code which was yet to be built. I simply copied and pasted the first viewController into a new file and called that mapViewController. I commented out the DynamicView code left over from the first controller and added the mapKit initialization code as shown here:
import UIKit
import MapKit
import CoreLocation
class mapViewController: UIViewController,CLLocationManagerDelegate,MKMapViewDelegate {
let DynamicView = UIView(frame: CGRect(x: UIScreen.main.bounds.maxX-110, y: UIScreen.main.bounds.maxY-110, width: 100, height: 100))
let switchView = UIView(frame: CGRect(x: UIScreen.main.bounds.minX+10, y: UIScreen.main.bounds.minY+20, width: 40, height: 40))
var mapView = MKMapView()
public var otherView:UIViewController? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = .white
// DynamicView.backgroundColor = .yellow
// DynamicView.layer.cornerRadius = 25
// DynamicView.layer.borderWidth = 2
switchView.backgroundColor = .orange
switchView.layer.borderWidth = 2
var gR = UITapGestureRecognizer(target: self, action:#selector(self.handlebigTap(_:)))
DynamicView.addGestureRecognizer(gR)
gR = UITapGestureRecognizer(target: self, action:#selector(self.handlesmallTap(_:)))
switchView.addGestureRecognizer(gR)
self.view.addSubview(switchView)
// self.view.addSubview(DynamicView)
// mapView.frame = CGRect(x: 10, y: 60, width: view.frame.size.width-20, height: 300)
mapView.mapType = MKMapType.standard
mapView.isZoomEnabled = true
mapView.isScrollEnabled = true
view.addSubview(mapView)
mapView.translatesAutoresizingMaskIntoConstraints = false
let margins = view.layoutMarginsGuide
mapView.leadingAnchor.constraint(equalTo: margins.leadingAnchor, constant: 10).isActive = true
mapView.trailingAnchor.constraint(equalTo: margins.trailingAnchor, constant: 0).isActive = true
mapView.topAnchor.constraint(equalTo: margins.topAnchor, constant: 45).isActive = true
mapView.bottomAnchor.constraint(equalTo: margins.bottomAnchor, constant: -20).isActive = true
}
#objc func handlebigTap(_ sender: UITapGestureRecognizer) {
if ( self.DynamicView.backgroundColor == .green){
self.DynamicView.backgroundColor = .blue
} else {
self.DynamicView.backgroundColor = .green
}
}
#objc func handlesmallTap(_ sender: UITapGestureRecognizer) {
if ( self.switchView.backgroundColor == .orange){
self.switchView.backgroundColor = .blue
dismiss(animated: true, completion: nil)
} else {
self.switchView.backgroundColor = .orange
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
}
}
I ran the above code and stepped through looking for the legal inset message to show up. This is the offending line:
mapView.frame = CGRect(x: 10, y: 60, width: view.frame.size.width-20, height: 300)
As soon as the view is framed, the error message comes out. Doesn't matter what dimensions you give the view, the message appears. OK.... Maybe the message expects a constraint based framing instead of hard coded per the offending line.
I commented out the line and added constraints to the view and still the error popped up.
At this point I gave up. I couldn't figure out how to configure a map view so the error message doesn't show up. Hopefully Apple will pipe up and say something at this point or someone else will pick up the baton to see if there's a way to configure mapkit to stop spewing error messages.
As for myself at least I learned how to dynamically add views, subviews, gesture recognizers and constraints so my time was profitably spent chasing the bug. Thanks to all who posted sample dynamic view code, gesture recognition code, subview code and constraint code. You may recognize a bit of your code here.
You have not typed in an opening curly bracket for your MapVC Class:
your code:
class MapVC: UIViewController
to fix:
class MapVC: UIViewController {

Google map loading issue iOS

I have integrated GoogleMap using pod in my project but map is not loading. When I make my mapViewController as a initial ViewController then its working fine.
But when I push or present MapViewController then its showing me the blank screen.
I am using XCode 9.1, my project deployment Target is 9.0 and Google Maps SDK for iOS version: 2.5.30219.0.
Thanks in Advance.
Here is my MapViewController code:
import UIKit
import GoogleMaps
class MapViewController: UIViewController {
#IBOutlet weak var mapView: GMSMapView!
override func viewDidLoad() {
super.viewDidLoad()
let camera = GMSCameraPosition.camera(withTarget: CLLocationCoordinate2DMake(18.486726, 73.798394), zoom: 10)
mapView.camera = camera
}
}
Console Log:
CoreData: annotation: Failed to load optimized model at path
/Users/pritamsing.salunkhe/Library/Developer/CoreSimulator/Devices/6DD7C0EA-320F-4E99-BEC9-672046198800/data/Containers/Bundle/Application/04CC747F-5B2D-4588-883A-E814D4EE0769/HimmatPlus.app/GoogleMaps.bundle/GMSCacheStorage.momd/StorageWithTileVersionID.omo
Don't use IBOutlet for googleMap in Storyboard & also Don't set subclass for GMSMapView.
implement your map by programmatically. Its will boost your Map UI faster
/// Google MapView
var mapView: GMSMapView!
self.mapView = GMSMapView(frame: CGRect(x: 0, y: 64, width: self.currentDeviceSize.width, height: self.bottomBgView.frame.minY - 64))
// self.mapView.autoresizingMask = [.flexibleBottomMargin, .flexibleTopMargin, .flexibleLeftMargin, .flexibleRightMargin]
self.mapView.settings.allowScrollGesturesDuringRotateOrZoom = true
// self.mapView.settings.myLocationButton = true //Enable My location button
// self.mapView.isTrafficEnabled = true //Enable traffic
// self.mapView.isBuildingsEnabled = true // Building Enable
// self.mapView.isMyLocationEnabled = true //My location Enabled
// let containerView: GMSMapView = self.mapView
self.view.addSubview(self.mapView)
Try with the proper implementation of Google map.
#IBOutlet weak var mapView: GMSMapView!
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
self.configureMap()
}
func configureMap(){
mapView.delegate = self
//Check for Location Services
if (CLLocationManager.locationServicesEnabled()) {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestWhenInUseAuthorization()
}
if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation()
}
}
MapView Delegate method
//MARK:- MapView delegate
extension YourViewControllerName: GMSMapViewDelegate, CLLocationManagerDelegate{
//Location Manager delegates
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last
let camera = GMSCameraPosition.camera(withLatitude: (location?.coordinate.latitude)!, longitude: (location?.coordinate.longitude)!, zoom: 12.0)
self.mapView?.animate(to: camera)
// To stop updating the location again
self.locationManager.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("error description:\(error.localizedDescription)")
}
}
Key In plist file.
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your custom message.</string>

How to detect the mapView was moved in Swift and update zoom

I'm trying to set a minimum zoom level on my map in Swift 2. I can't find any documentation on how to restrict a map from being zoomed too far in. What I've decided to try is to monitor for map movement (such as drag or zoom) and then set MKZoomScaleback to a minimum.
Most of the answers I've found for regionDidChangeAnimated are in Objective C, which I don't know and I'm having trouble converting them to Swift.
I tried implementing #hEADcRASH's answer: https://stackoverflow.com/a/30924768/4106552, but it doesn't trigger and print anything to the console when the map is moved in the simulator.
Can anyone tell me what I'm doing wrong? I'm new to Swift, so it could be a small error. Also, let me know if there is a lightweight way to solve for restricting the zoom level on a map. I'm worried that the monitor for movement will slow down the map animation a bit. Thanks for the help.
Here is my view controller.
import UIKit
import Parse
import MapKit
class SearchRadiusViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var map: MKMapView!
#IBOutlet weak var menuBtn: UIBarButtonItem!
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
//menu button control
if self.revealViewController() != nil {
menuBtn.target = self.revealViewController()
menuBtn.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
//user location
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//set map
let location:CLLocationCoordinate2D = manager.location!.coordinate
let latitude = location.latitude
let longitude = location.longitude
let latDelta:CLLocationDegrees = 0.1
let longDelta:CLLocationDegrees = 0.1
let span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, longDelta)
let maplocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(maplocation, span)
map.setRegion(region, animated: true)
//stop updating location, only need user location once to position map.
manager.stopUpdatingLocation()
}
//Attempt to monitor for map movement based on hEADcRASH's answer.
private var mapChangedFromUserInteraction = false
private func mapViewRegionDidChangeFromUserInteraction() -> Bool {
let view = self.map.subviews[0]
// Look through gesture recognizers to determine whether this region change is from user interaction
if let gestureRecognizers = view.gestureRecognizers {
for recognizer in gestureRecognizers {
if( recognizer.state == UIGestureRecognizerState.Began || recognizer.state == UIGestureRecognizerState.Ended ) {
return true
}
}
}
return false
}
func mapView(mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
print("yes")
mapChangedFromUserInteraction = mapViewRegionDidChangeFromUserInteraction()
if (mapChangedFromUserInteraction) {
// user changed map region
print("user changed map in WILL")
}
}
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
print("yes ddd")
if (mapChangedFromUserInteraction) {
// user changed map region
print("user changed map in Did")
}
}
}
After reviewing and combining a number of other questions/answers and the help of #lorenzoliveto I've got it working in Swift. Please leave a comment if there a better/more lightweight way to achieve the same thing.
I added self.map.delegate = self to the viewDidLoad function.
Below is the code for how I'm monitoring for map movement and then once a user has zoomed in "too far" and the width of the map goes below 2 miles I then zoom out the map using mapView.setRegion.
private var mapChangedFromUserInteraction = false
private func mapViewRegionDidChangeFromUserInteraction() -> Bool {
let view = self.map.subviews[0]
// Look through gesture recognizers to determine whether this region change is from user interaction
if let gestureRecognizers = view.gestureRecognizers {
for recognizer in gestureRecognizers {
if( recognizer.state == UIGestureRecognizerState.Began || recognizer.state == UIGestureRecognizerState.Ended ) {
return true
}
}
}
return false
}
func mapView(mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
mapChangedFromUserInteraction = mapViewRegionDidChangeFromUserInteraction()
if (mapChangedFromUserInteraction) {
// user will change map region
print("user WILL change map.")
// calculate the width of the map in miles.
let mRect: MKMapRect = mapView.visibleMapRect
let eastMapPoint = MKMapPointMake(MKMapRectGetMinX(mRect), MKMapRectGetMidY(mRect))
let westMapPoint = MKMapPointMake(MKMapRectGetMaxX(mRect), MKMapRectGetMidY(mRect))
let currentDistWideInMeters = MKMetersBetweenMapPoints(eastMapPoint, westMapPoint)
let milesWide = currentDistWideInMeters / 1609.34 // number of meters in a mile
print(milesWide)
print("^miles wide")
// check if user zoomed in too far and zoom them out.
if milesWide < 2.0 {
var region:MKCoordinateRegion = mapView.region
var span:MKCoordinateSpan = mapView.region.span
span.latitudeDelta = 0.04
span.longitudeDelta = 0.04
region.span = span;
mapView.setRegion(region, animated: true)
print("map zoomed back out")
}
}
}
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
if (mapChangedFromUserInteraction) {
// user changed map region
print("user CHANGED map.")
print(mapView.region.span.latitudeDelta)
print(mapView.region.span.longitudeDelta)
// calculate the width of the map in miles.
let mRect: MKMapRect = mapView.visibleMapRect
let eastMapPoint = MKMapPointMake(MKMapRectGetMinX(mRect), MKMapRectGetMidY(mRect))
let westMapPoint = MKMapPointMake(MKMapRectGetMaxX(mRect), MKMapRectGetMidY(mRect))
let currentDistWideInMeters = MKMetersBetweenMapPoints(eastMapPoint, westMapPoint)
let milesWide = currentDistWideInMeters / 1609.34 // number of meters in a mile
print(milesWide)
print("^miles wide")
// check if user zoomed in too far and zoom them out.
if milesWide < 2.0 {
var region:MKCoordinateRegion = mapView.region
var span:MKCoordinateSpan = mapView.region.span
span.latitudeDelta = 0.04
span.longitudeDelta = 0.04
region.span = span;
mapView.setRegion(region, animated: true)
print("map zoomed back out")
}
}
UPDATE: 3/7, I discovered an interesting bug in the implementation above. On the simulator it works fine when clicking to zoom, but when you use the pinch to zoom (option + click) the simulator stops allowing you to drag the map around after it animates the zoom back out. This also happened on the beta version on my iphone. I added dispatch_async around the blocks that animate that map back to their position and it appears to be working on the simulator. It no longer appears frozen after it animates and I can continue to drag around the map and try to zoom in.
dispatch_async(dispatch_get_main_queue(), {
var region:MKCoordinateRegion = mapView.region
var span:MKCoordinateSpan = mapView.region.span
span.latitudeDelta = 0.04
span.longitudeDelta = 0.04
region.span = span;
mapView.setRegion(region, animated: true)
print("map zoomed back out")
})
The solution that works for me is one where I set the zoom range. This approach may not have been available at the time the question was asked.
The code fragment below is what I use. I'm not entirely sure what the distance units are, but I believe they are meters. Figuring out what range works in a given instance may be a matter of trial and error.
let mapView = MKMapView(frame: .zero)
let zoomRange = MKMapView.CameraZoomRange(
minCenterCoordinateDistance: 120000,
maxCenterCoordinateDistance: 1600000
)
mapView.cameraZoomRange = zoomRange

Resources