Zoom in on User Location- Swift - ios

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

Related

SearchBar not showing on navigation bar

I'm trying to show the search bar on the navigation bar.
The search bar should show me the search results and then show me the map navigation details.
I tried the same code to another project and seems to work.
This happens with the latest version of Xcode 10 and iOS 12.
here is the code:
// Protocol for dropping a pin at a specified place
protocol HandleMapSearch: class {
func dropPinZoomIn(_ placemark:MKPlacemark)
}
class MapViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
#IBAction func navigateButton(_ sender: Any) {
}
let locationManager = CLLocationManager()
var currentLocation: CLLocation?
var routeCoordinates = [CLLocationCoordinate2D]()
var resultSearchController: UISearchController!
var selectedPin: MKPlacemark?
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
mapView.showsUserLocation = true
let coordinateSpan = MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
guard let userCoordinate = locationManager.location?.coordinate else { return }
let userRegion: MKCoordinateRegion = MKCoordinateRegion(center: userCoordinate, span: coordinateSpan)
// Zoom to user location
mapView.setRegion(userRegion, animated: true)
// Hide back button
self.navigationItem.setHidesBackButton(true, animated: false)
let locationSearchTable = storyboard!.instantiateViewController(withIdentifier: "LocationSearchTable") as! LocationSearchTable
resultSearchController = UISearchController(searchResultsController: locationSearchTable)
resultSearchController.searchResultsUpdater = locationSearchTable
let searchBar = resultSearchController!.searchBar
searchBar.sizeToFit()
searchBar.placeholder = "Search for places"
navigationItem.titleView = resultSearchController?.searchBar
resultSearchController.hidesNavigationBarDuringPresentation = false
resultSearchController.dimsBackgroundDuringPresentation = true
definesPresentationContext = true
locationSearchTable.mapView = mapView
locationSearchTable.handleMapSearchDelegate = self
}
I get no errors and not console log messages

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

Zooming Effect with MKMapView on IBAction

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.

Keep the pin (MKAnnotation) on the center when user scroll the map

I would like keep the MKAnnotaion on the centre of the screen when the user scoll the map like Careem app:
So far I managed to show the pin, update the pin position but when I scroll the map, the annotation in my code initially moves and then gets back to the centre. I would like the pin to remain on the centre.
#IBOutlet var mapView: MKMapView!
var centerAnnotation = MKPointAnnotation()
var manager:CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
manager = CLLocationManager() //instantiate
manager.delegate = self // set the delegate
manager.desiredAccuracy = kCLLocationAccuracyBest // required accurancy
manager.requestWhenInUseAuthorization() // request authorization
manager.startUpdatingLocation() //update location
var lat = manager.location.coordinate.latitude // get lat
var long = manager.location.coordinate.longitude // get long
var coordinate = CLLocationCoordinate2DMake(lat, long)// set coordinate
var latDelta:CLLocationDegrees = 0.01 // set delta
var longDelta:CLLocationDegrees = 0.01 // set long
var span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, longDelta)
var region:MKCoordinateRegion = MKCoordinateRegionMake(coordinate, span)
self.mapView.setRegion(region, animated: true)
centerAnnotation.coordinate = mapView.centerCoordinate
self.mapView.addAnnotation(centerAnnotation)
}
func mapView(mapView: MKMapView!, regionDidChangeAnimated animated: Bool) {
centerAnnotation.coordinate = mapView.centerCoordinate;
}
****UPDATE****
As suggested by Anna I have added a view to the map. Here the code:
var newPoint = self.mapView.convertCoordinate(mapView.centerCoordinate, toPointToView: self.view)
var pinImage = UIImage(named: "yoga_pins.png")
var imageView = UIImageView(image: pinImage) // set as you want
imageView.image = pinImage
imageView.backgroundColor = UIColor.clearColor()
imageView.contentMode = UIViewContentMode.Center
imageView.center.y = newPoint.y
imageView.center.x = newPoint.x
self.view.addSubview(imageView)
the only problem is that when the map is loaded the first time, the annotation which is located on the mapView.centerCoordinate and I am gonna use to get the latitude and longitude:
when I then scroll the map, the pin moves in the correct position (under the image):
func mapView(mapView: MKMapView!, regionDidChangeAnimated animated: Bool) {
centerAnnotation.coordinate = mapView.centerCoordinate;
}
I recommend you DSCenterPinMapView
It is a custom MapView with an animated and customizable center pin useful for selecting locations in map.
You should install the Pod and then, as a solution to your question you should implement the delegate so that you can get the location where pin drops.
pinMapView.delegate = self
extension MyViewController: DSCenterPinMapViewDelegate {
func didStartDragging() {
// My custom actions
}
func didEndDragging() {
// My custom actions
selectedLocation = pinMapView.mapview.centerCoordinate
}
}
You can use custom button. Add that button on centre of the map. Just show and hide that button according to your conditions.
So when you move map that button stay on same position that is centre of the map.

Resources