Zooming Effect with MKMapView on IBAction - ios

I'm trying to find some code that will have the following effect. I currently have a map setup in my view, and a button simply named "Zoom Out". Upon clicking this button, I would like the MKCoordinateSpanMake to go from (0.008, 0.008) to (0.05, 0.05), for example.
Here is the code I have so far:
AboutMeViewController.swift
import MapKit
class AboutMeViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapOfMySchool: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
let location = CLLocationCoordinate2D(
latitude: XX.4890669,
longitude: -XX.6993226
)
let span = MKCoordinateSpanMake(0.008, 0.008)
let region = MKCoordinateRegion(center: location, span: span)
mapOfMySchool.setRegion(region, animated: true)
let annotation = MKPointAnnotation()
annotation.coordinate = location
annotation.title = "School Name"
annotation.subtitle = "Subtitle"
mapOfMySchool.addAnnotation(annotation)
}
#IBAction func zoomInMap(sender: UIButton) {
// Zoom code here
}
}

Have you tried:
#IBAction func zoomInMap(sender: UIButton) {
let location = CLLocationCoordinate2D(
latitude: XX.4890669,
longitude: -XX.6993226
)
let span = MKCoordinateSpanMake(0.05, 0.05)
let region = MKCoordinateRegion(center: location, span: span)
mapOfMySchool.setRegion(region, animated: true)
}
I would also suggest changing the call in viewDidLoad to be unanimated since it won't be visible to the user anyway.

Related

how to delete one overlay when another is added in Xcode swift 3

I'm trying to build a function in my app which allows the user to drop a pin in a location and show a radius around that location. I only want one pin and one radius to show at a time.
I have worked out how to drop a pin and add the radius, and to remove the old pin when the new one is dropped, but cannot work out how to delete the circle overlay so that only one is show, around the most recent dropped pin.
Code is below for my map view controller. help much appreciated!
import UIKit
import CoreLocation
import MapKit
class MapVC: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
//connect map
#IBOutlet weak var mapInterface: MKMapView!
let manager = CLLocationManager()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let location = locations[0] //all locations will be stored in CLLocation array, we request 0th element (the newest data which equals the most recent location)
print(location)
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.05, 0.05)
let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span)
print(location)
mapInterface.setRegion(region, animated: true)
self.mapInterface.showsUserLocation = true
}
//add pin drop
#IBAction func pinDrop(_ sender: UILongPressGestureRecognizer) {
//Pin drop annotation - get attributes of where to drop the pin
let location = sender.location(in: self.mapInterface)
let locCoord = self.mapInterface.convert(location, toCoordinateFrom: self.mapInterface)
let annotation = MKPointAnnotation()
//set pin characteristics
annotation.coordinate = locCoord
annotation.title = "Virtual location"
annotation.subtitle = "Dropped Pin"
//delete one pin once another is dropped
self.mapInterface.removeAnnotations(mapInterface.annotations)
//add pin annotation to map view
self.mapInterface.addAnnotation(annotation)
//print locCoord to console to check it worked
print(locCoord)
//create circle attributes
let cent = locCoord
let rad: Double = 500 //adjust radius to make circle bigger.
let circle = MKCircle(center: cent, radius: rad)
//print circle to console to check it worked
print(circle)
}
//add circle overlay
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay.isKind(of: MKCircle.self){
let circleRenderer = MKCircleRenderer(overlay: overlay)
circleRenderer.fillColor = UIColor.blue.withAlphaComponent(0.05)
circleRenderer.strokeColor = UIColor.blue
circleRenderer.lineWidth = 0.5
return circleRenderer
}
self.mapInterface.removeOverlays(overlay as! [MKOverlay])
return MKOverlayRenderer(overlay: overlay)
}
override func viewDidLoad() {
super.viewDidLoad()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
manager.stopUpdatingLocation()
}
}
Add the following code in pinDrop method.
self.mapInterface.removeOverlays(self.mapInterface.overlays)
mapView.removeOverlays(mapView.overlays) will do the trick
however, if i were you i'd rather update the annotation location instead of recreating a new one and remove the old one
to do that you will take that line
let annotation = MKPointAnnotation()
and put it above viewDidLoad method
and instead of :
//delete one pin once another is
self.mapInterface.removeAnnotations(mapInterface.annotations)
//add pin annotation to map view
self.mapInterface.addAnnotation(annotation)
it will be :
if self.mapInterface.annotations.count == 0 {
self.mapInterface.addAnnotation(annotation)
}

Swift span zoom

Need simple coding for the span to zoom on the users location when the button is pressed. It follows the user but need span coding help.
i use this atm
#IBAction func Refreshbutton(sender: AnyObject) {
//Navigationsknappen
Mapview.userTrackingMode = .Follow
self.Locationmanager.stopUpdatingLocation()
}
This code may help you.
let span = MKCoordinateSpanMake(0.075, 0.075)
let region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude:lat, longitude: long), span: span)
mapView.setRegion(region, animated: true)
My understanding is, Its not that simple -
You need to create a region - MKCoordinateRegion
Get long and lat using MKCoordinateSpan
example:
func mapView(mapView: MKMapView!, regionDidChangeAnimated animated: Bool) {
//Get the location you need the span zoom
let location = CLLocationCoordinate2D(
//getPropertyLocation.latitude/longitude already set to CLLocationDegrees in a prior process.
latitude: self.getPropertyLocation.latitude ,
longitude: self.getPropertyLocation.longitude
)
// Get the span that the mapView is set to by the user. "propertyMapView" is the MKMapView in this example.
let span = self.propertyMapView.region.span
// Setup the region based on the lat/lon of the property and retain the span that already exists.
let region = MKCoordinateRegion(center: location, span: span)
//Center the view with some animation.
mapView.setRegion(region, animated: true)
}

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) error occurs when getting the latitude and longitude

Please help me with this problem
import Foundation
import UIKit
import MapKit
class DetailViewController : UIViewController {
#IBOutlet weak var mapView: MKMapView!
var selectedLocation : LocationModel?
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
// Create coordinates from location lat/long
var poiCoodinates: CLLocationCoordinate2D = CLLocationCoordinate2D()
poiCoodinates.latitude = CDouble(self.selectedLocation!.latitude!)! //Problem is in this line
poiCoodinates.longitude = CDouble(self.selectedLocation!.longitude!)!
// Zoom to region
let viewRegion: MKCoordinateRegion = MKCoordinateRegionMakeWithDistance(poiCoodinates, 750, 750)
self.mapView.setRegion(viewRegion, animated: true)
// Plot pin
let pin: MKPointAnnotation = MKPointAnnotation()
pin.coordinate = poiCoodinates
self.mapView.addAnnotation(pin)
//add title to the pin
pin.title = selectedLocation!.name
}
}
You have not initialised var selectedLocation : LocationModel? so when you ask for self.selectedLocation! it crash.
Add that needed initialisation and try to refactor your code in this
way:
override func viewDidAppear(animated: Bool) {
guard let location = self.selectedLocation, let latitude = location.latitude, let longitude = location.longitude else {
return //Here was an error, so you can not continue, report it or do something about it before returning
}
// Create coordinates from location lat/long
var poiCoodinates: CLLocationCoordinate2D = CLLocationCoordinate2D()
poiCoodinates.latitude = CDouble(latitude)!
poiCoodinates.longitude = CDouble(longitude)!
// Zoom to region
let viewRegion: MKCoordinateRegion = MKCoordinateRegionMakeWithDistance(poiCoodinates, 750, 750)
self.mapView.setRegion(viewRegion, animated: true)
// Plot pin
let pin: MKPointAnnotation = MKPointAnnotation()
pin.coordinate = poiCoodinates
self.mapView.addAnnotation(pin)
//add title to the pin
pin.title = location.name
}
I finally found the solution and here it goes:
import MapKit
class DetailViewController : UIViewController, MKMapViewDelegate {
//var mapType:UISegmentedControl!
//var showPointsOfInterest:UISwitch!
#IBOutlet weak var mapView: MKMapView!
#IBAction func showDirection(sender: AnyObject) {
}
var selectedLocation : LocationModel?
let locationManager = CLLocationManager()
var currentPlacemark:CLPlacemark?
//var segmentedControlAciton:UISegmentedControl!
#IBAction func myLocation(sender: AnyObject) {
// Request for a user's authorization for location services
locationManager.requestWhenInUseAuthorization()
let status = CLLocationManager.authorizationStatus()
if status == CLAuthorizationStatus.AuthorizedWhenInUse {
mapView.showsUserLocation = true
}
}
//
override func viewDidLoad() {
super.viewDidLoad()
// mapView.showsUserLocation = true
title = selectedLocation?.name
mapView.delegate = self
}
override func viewDidAppear(animated: Bool) {
// Create coordinates from location lat/long
var poiCoordinates: CLLocationCoordinate2D = CLLocationCoordinate2D()
poiCoordinates.latitude = CDouble(self.selectedLocation!.latitude!)!
poiCoordinates.longitude = CDouble(self.selectedLocation!.longitude!)!
// Zoom to region
let viewRegion: MKCoordinateRegion = MKCoordinateRegionMakeWithDistance(poiCoordinates, 750, 750)
self.mapView.setRegion(viewRegion, animated: true)
// Plot pin
let pin: MKPointAnnotation = MKPointAnnotation()
pin.coordinate = poiCoordinates
self.mapView.addAnnotation(pin)
//add title to the pin
pin.title = selectedLocation!.name
pin.subtitle=selectedLocation!.address
mapView.showsScale = 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