Thread 1: EXC_BREAKPOINT (code=1, subcode=...) When using MKMap View - ios

So I recently encountered a problem with my MKMapView. Whenever I click a button to go to the view with the map in it I get the above error centered around this line.
Updated with full script. error is let region = MKCoordinateRegion.init(center: (userLocation.location?.coordinate)!, latitudinalMeters: 5000, longitudinalMeters: 5000)
mapView.setRegion(region, animated: true) }
#IBOutlet weak var mapView: MKMapView!
#IBAction func mapTypeChanged(_ sender: Any) {
switch ((sender as AnyObject).selectedSegmentIndex) {
case 0:
mapView.mapType = .standard
case 1:
mapView.mapType = .hybridFlyover
default:
mapView.mapType = .standard
}
}
#IBAction func zoomIn(_ sender: Any) {
let userLocation = mapView.userLocation
let region = MKCoordinateRegion.init(center: (userLocation.location?.coordinate)!, latitudinalMeters: 5000, longitudinalMeters: 5000)
self.mapView.setRegion(region, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
mapView.showsUserLocation = true
mapView.delegate = self
let userLocation = mapView.userLocation
let region = MKCoordinateRegion.init(center: (userLocation.location?.coordinate)!, latitudinalMeters: 5000, longitudinalMeters: 5000)
mapView.setRegion(region, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
There was a formatting problem.
I have no clue why this is as it wasnt happening before. Could anybody help out? Any help will be greatly appreciated

You can try this
if let coor = userLocation.location?.coordinate {
let region = MKCoordinateRegion(center:coor, latitudinalMeters: 5000, longitudinalMeters: 5000)
}
it will work if there is a current value for coor , maybe you click it before getting a location so request for location updates again in else , or initially inside viewDidLoad
//
Use this method
var once = true
//
func mapView(_ mapView: MKMapView,
didUpdate userLocation: MKUserLocation) {
if once {
let region = MKCoordinateRegion.init(center: userLocation.location!.coordinate, latitudinalMeters: 5000, longitudinalMeters: 5000)
mapView.setRegion(region, animated: true)
once = false
}
}
of the delegateMKMapViewDelegate to get the correct value

Related

location manager is not on main thread after segue?

I have a serious problem about use location manage and mapview.
I create a app simple show where the location I am now, it runs ok when only one viewcontroller, but after I added a viewController and segue, and after segue first viewController to map viewController, it has some error message:
location manager (0x101f3d870) was created on a dispatch queue executing on a thread other than the main thread
after that, the App crash with this error message :
MKMapView must be initialized on the main thread
But I really move the code into the main thread, and it does ok before I added first viewcontroller and segue!!! The code at below:
import UIKit
import MapKit
import CoreLocation
class Map_ViewController: UIViewController,MKMapViewDelegate,CLLocationManagerDelegate,UISearchBarDelegate {
#IBOutlet weak var mapView: MKMapView!
var cloaction = CLLocationManager()
var span = MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
var currentLat : Double = 0
var currentLon : Double = 0
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
// Start get the location on viewWillAppear
DispatchQueue.main.async {
self.mapView.delegate = self
self.mapView.showsScale = true
self.mapView.showsPointsOfInterest = true
self.mapView.showsUserLocation = true
self.cloaction.requestAlwaysAuthorization()
self.cloaction.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
self.cloaction.delegate = self
self.cloaction.desiredAccuracy = kCLLocationAccuracyBest
self.cloaction.startUpdatingLocation()
}
setCenter()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
mapView.region.center = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
mapView.setRegion(mapView.region, animated: false)
}
}
func setCenter() {
let center = CLLocationCoordinate2D(latitude: mapView.userLocation.coordinate.latitude, longitude: mapView.userLocation.coordinate.longitude)
mapView.region.center = center
mapView.region.span = span
mapView.setRegion(mapView.region, animated: true)
}
}
The segue in the main viewController :
override func viewDidLoad() {
super.viewDidLoad()
self.performSegue(withIdentifier: "goToMap", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("goToMapViewController")
}

How do I allow the user to resize an mkcircle object in a map view using a gesture recognizer on the mkcircle?

In Appleā€™s iOS Reminders app, you can set a location area at which you would be reminded of your reminder. When you set the location area, you are allowed to draw a circle that covers the area of the location you want to be reminded at. How would I duplicate this feature. I particularly would like to know how to allow the user to resize the circle on a map view by dragging his finger on the edge of the circle. I have already figured out how to create the circle.
Here is my code so far:
import UIKit
import MapKit
class ViewController: UIViewController {
// MARK: - Outlets
#IBOutlet weak var mapView: MKMapView!
// MARK: - Variables and Constants
let regionRadius: CLLocationDistance = 1000
let circularRegionRadius: CLLocationDistance = 500
let locationManager = CLLocationManager()
// MARK: - View
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
centerMapOnLocation(location: locationManager.location!)
}
// MARK: - Actions
#IBAction func addRegion(_ sender: UILongPressGestureRecognizer) {
print("addRegion(_:)")
let longPress = sender
let touchLocation = longPress.location(in: mapView)
let coordinates = mapView.convert(touchLocation, toCoordinateFrom: mapView)
let region = CLCircularRegion(center: coordinates, radius: circularRegionRadius, identifier: "geofence")
mapView.removeOverlays(mapView.overlays)
locationManager.startMonitoring(for: region)
let circle = MKCircle(center: coordinates, radius: region.radius)
mapView.add(circle)
}
// MARK: - Helper Functions
func centerMapOnLocation(location: CLLocation) {
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,
regionRadius, regionRadius)
mapView.setRegion(coordinateRegion, animated: true)
}
// MARK: - Memory Warning
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
print("mapView(_:rendererFor:_:)")
guard let circelOverLay = overlay as? MKCircle else {return MKOverlayRenderer()}
let circleRenderer = MKCircleRenderer(circle: circelOverLay)
circleRenderer.strokeColor = .blue
circleRenderer.fillColor = .blue
circleRenderer.alpha = 0.2
return circleRenderer
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("locationManager(_:didUpdateLocations:)")
locationManager.stopUpdatingLocation()
mapView.showsUserLocation = true
}
}

Creating a tableViewCell with a mapView

I'm creating a tableViewCell with a mapView inside. however i can't seem to add a annotation based on a location in the viewController containing the tableView. i've started by in the cellForRowAtIndex by setting cell.location = orgObject.coordinate after that i've added following code to my custom cell. However location variable keep returning nil. i guess this is because awakeForNib is called before cellForRowAtIndexPath is called? how do i solve this?
import UIKit
import MapKit
class MapTableViewCell: UITableViewCell, MKMapViewDelegate {
var location: CLLocation?
#IBOutlet var mapView: MKMapView?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
//MapView
mapView!.showsPointsOfInterest = true
if let mapView = self.mapView
{
mapView.delegate = self
}
let orgLocation = CLLocationCoordinate2DMake(location!.coordinate.latitude, location!.coordinate.longitude)
let dropPin = MKPointAnnotation()
dropPin.coordinate = orgLocation
mapView!.addAnnotation(dropPin)
self.mapView?.setRegion(MKCoordinateRegionMakeWithDistance(orgLocation, 500, 500), animated: true)
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialisation code
//MapView
mapView!.showsPointsOfInterest = true
if let mapView = self.mapView
{
mapView.delegate = self
}
}
Call cell.showLocation(location) from cellForRowAtIndexPath
func showLocation(location:CLLocation) {
let orgLocation = CLLocationCoordinate2DMake(location!.coordinate.latitude, location!.coordinate.longitude)
let dropPin = MKPointAnnotation()
dropPin.coordinate = orgLocation
mapView!.addAnnotation(dropPin)
self.mapView?.setRegion(MKCoordinateRegionMakeWithDistance(orgLocation, 500, 500), animated: true)
}

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

Zoom in on User Location- Swift

I can't figure out how to get the map to zoom in on the user location from viewDidLoad. I tried setting a region, and that didn't work. This is the code I have, any tips?
#IBOutlet weak var mapView: MKMapView!
var MapViewLocationManager:CLLocationManager! = CLLocationManager()
var currentLocation: PFGeoPoint! = PFGeoPoint()
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.showsUserLocation = true
mapView.delegate = self
MapViewLocationManager.delegate = self
mapView.setUserTrackingMode(MKUserTrackingMode.Follow, animated: true)
}
I have looked up the answers for this question but haven't found the answer in Swift or that actually works. Thanks!!
I would try something like this:
let latitude:CLLocationDegrees = //insert latitutde
let longitude:CLLocationDegrees = //insert longitude
let latDelta:CLLocationDegrees = 0.05
let lonDelta:CLLocationDegrees = 0.05
let span = MKCoordinateSpanMake(latDelta, lonDelta)
let location = CLLocationCoordinate2DMake(latitude, longitude)
let region = MKCoordinateRegionMake(location, span)
mapView.setRegion(region, animated: false)
I saw you said you tried setting a region. Maybe try doing it this way.
On most apps, a 'current location' button is implemented. A simple way to do it is like this:
#IBAction func myLocationButtonTapped(_ sender: Any) {
mapView.showsUserLocation = true
mapView.setUserTrackingMode(.follow, animated: true)
}
Once the user pans or zooms the map, the tracking will stop. So it's good enough for me.
You have to try this.
It will zooming map.
let span = MKCoordinateSpanMake(0.050, 0.050)
let region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 23.0225, longitude: 72.5714), span: span)
mapView.setRegion(region, animated: true)`
#IBOutlet weak var conscienceMap: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
conscienceMap.showsUserLocation = true
conscienceMap.setUserTrackingMode(MKUserTrackingMode.follow, animated: true)
}

Resources