I am using this to zoom into the users location at startup.
func locationManager(manager:CLLocationManager, didUpdateLocations locations:[AnyObject]) {
var locationArray = locations as NSArray
var locationObj = locationArray.lastObject as CLLocation
var coord = locationObj.coordinate
var newRegion = MKCoordinateRegion(center: coord, span: MKCoordinateSpanMake(spanX, spanY))
mapView.setRegion(newRegion, animated: true)
}
Now I'd like to take an action if the map has been zoomed into the users location. I've tried to use the mapDidChange delegate method, but this is fired a few times. Any thoughts how to do this the clever way? (iOS 8 / Swift)
Just In my opinion , you can compare newRegion's span values with map view's current span values in regionDidChangeAnimated. If span values are the same, you can secondly play with "mapview.region". May be getting visible mapView.region and comparing it with newRegion or getting centre coordinate of current visible region and comparing it with current location's coordinate can work.
Related
I am in a confusion, on how to get a zoom level and radius of a visible area of the map (using mapkit mapview).
Here is what I am looking for (either of them or both, as needed) -
Zoom level, is to see at what level of the map is being shown to the users and with that information, I want to display the custom pins in the visible area. If zoom level is high, I need to show the actual logos of some commerce stores as pins. If zoom level is low, I need to show colored dots instead of logos.
As of now, I am using let updatedRadius = (mapView.camera.altitude)/1000 to get altitude of the camera, and if the updatedRadius value is > 25.0, I am showing colored dots. Below 25.0, I show the logos. I am doing this in regionDidChanged
Is this approach correct?
Radius, is to send it as a parameter to my REST API to fetch the list of places within that radius. When user zooms out on the map, visible area increases and so the REST API needs bigger radius to return the places covered in that area.
Ultimately, what should happen is, whenever user zooms out, then the radius changes. I need to send this changed radius to my REST to get an updated list.
What are latitude longtitude deltas, can we get radius/width of visible area using these values?
let latitudeDeltaVal = mapView.region.span.latitudeDelta
let longitudeDeltaVal = mapView.region.span.longitudeDelta
Can someone throw some light on what needs to be done please?
Since you need to call the api when the region changes you need to calculate the radius in mapView's delegate function, RegionDidChange.
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
let centralLocation = CLLocation(latitude: mapView.centerCoordinate.latitude, longitude: mapView.centerCoordinate.longitude)
self.centralLocationCoordinate = mapView.centerCoordinate
print("Radius - \(self.getRadius(centralLocation))")
}
func getRadius(centralLocation: CLLocation) -> Double{
let topCentralLat:Double = centralLocation.coordinate.latitude - mapView.region.span.latitudeDelta/2
let topCentralLocation = CLLocation(latitude: topCentralLat, longitude: centralLocation.coordinate.longitude)
let radius = centralLocation.distanceFromLocation(topCentralLocation)
return radius / 1000.0 // to convert radius to meters
}
To account for both landscape and portrait orientations, and/or situations where the map orientation is close to Northeast, Northwest, Southwest, Southeast, and to enclose the full screen up to the corners, one should consider both latitudeDelta and longitudeDelta:
func getRadius() -> Double{
let centralLocation = CLLocation(latitude: mapView.region.center.latitude, longitude: mapView.region.center.longitude)
let cornerOfMap = CLLocation(latitude: centralLocation.coordinate.latitude + mapView.region.span.latitudeDelta , longitude: centralLocation.coordinate.longitude + mapView.region.span.longitudeDelta)
let radius = centralLocation.distance(from: cornerOfMap)
return radius / 1000.0 // to convert radius to meters
}
I had to show locations around the user location. The locations info will arrive from the server. I am using 'for' loop to parse the location and add to map with annotation pin. As of now, it shows only the last arrived location. But the requirement is such that multiple locations should be shown with user location as Center point. As of now I am able to show only one pin. Please help how to achieve this?
for (NSDictionary* dictionary in responseDict)
{
NSString *latitudeString=[NSString stringWithFormat:#"%#",[dictionary valueForKey:#"LATITUDE"]];
double latitude=[latitudeString doubleValue];
NSString *longitudeString=[NSString stringWithFormat:#"%#",[dictionary valueForKey:#"LONGITUDE"]];
double longitude=[longitudeString doubleValue];
NSLog(#"the LATITUDE AND LONGITUDE is %f, %f",latitude,longitude);
CLLocationCoordinate2D locationCoordinate;
locationCoordinate.latitude=latitude;
locationCoordinate.longitude=longitude;
[self.mapView addAnnotation:locationCoordinate withPinColor:WKInterfaceMapPinColorPurple];
MKCoordinateSpan coordinateSpan = MKCoordinateSpanMake(0.05, 0.05);
[self.mapView setRegion:(MKCoordinateRegionMake(locationCoordinate, coordinateSpan))];
}
`
You zoom on a single location in each iteration of the loop, so eventually you end up zoomed on the last location. Sadly WKInterfaceMap doesn't have a showAnnotations method as MKMapView does, so you need to write a function yourself to achieve showing an MKCoordinateRegion including several annotations.
I haven't written any Obj-C for a while, but here's the function in Swift that shows two annotations on a Map and works on watchOS:
func showTwoMapCoordinates(first: CLLocationCoordinate2D, second: CLLocationCoordinate2D)->MKCoordinateRegion{
let center = CLLocationCoordinate2D(latitude: (first.latitude+second.latitude)/2, longitude: (first.longitude+second.longitude)/2)
var latDelta:CLLocationDegrees {
let delta = abs((first.latitude-second.latitude)*1.4)
if delta > 0 {
return delta
} else {
return 0.1
}
}
var lonDelta:CLLocationDegrees {
let delta = abs((first.longitude-second.longitude)*1.4)
if delta > 0 {
return delta
} else {
return 0.1
}
}
let span = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lonDelta)
return MKCoordinateRegion(center: center, span: span)
}
To extend this to n points, where n>2, all you need to do is iterate through each pair of points you want to display, find the maximum deltas the same way as in the function above and set the center and span using the function above called on the two points that yielded the max deltas.
MKMapView
CoreLocation
Swift3.0
I have a requirement like this:
Creating an application in which I am getting Lat and Long for various location from webservice . Ploting these locations on map with Custom AnnotationView . Apart from this, I'm showing User Current Location with Custom Image.
Here everything working fine.
Now, when user moves then User Current Location Pin changes its position so there is an important requirement for this, I need to make this Current Location position in such a way that it stick in the bottom of map (just 100 pixel) above from bottom.
I did Some RND and find out few useful links:
Centering MKMapView on spot N-pixels below pin
How can I center my mapview so that the selected pin is not in the middle of the map but in 1/3 of it?
set current location icon lower side in MKMapView
But this not working anymore.
Kindly suggest how can I set user Current Location pin in iOS map in such a way that it always at the bottom of Map (100 pixel above from Bottom)
Required Pin position:
Currently it is showing in other position.
Note - **Dont want Google Map for such functionality.
If you have been using something like this mapView.setUserTrackingMode(.Follow, animated: true) then user location will be always displayed in the center of the map, zoom/pan will be automatically adjusted whenever user location is updated.
You need to disable the UserTrackingMode and then manually adjust the map zoom/pan using func setVisibleMapRect(_ mapRect: MKMapRect, edgePadding insets: UIEdgeInsets, animated animate: Bool) each time whenever user location is changed.
I used following code in similar situation
let annotationPoint = MKMapPointForCoordinate(mapView.userLocation.coordinate);
var zoomRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 1, 1);
annotationPoint = MKMapPointForCoordinate(annotation.coordinate);
let pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 1, 1);
zoomRect = MKMapRectUnion(zoomRect, pointRect);
mapView.setVisibleMapRect(zoomRect, edgePadding: UIEdgeInsetsMake(40, 50, 160, 50), animated: true)
p.s. edgePadding is your friend in this case.
Here is the trick to use to position the map in such a way that UserLocation pin gets adjusted automatically :
func setUserLocationOnLowerPositiononMap(coordinate: CLLocationCoordinate2D) {
var region = MKCoordinateRegion(center: coordinate, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
region.center = coordinate
self.map.setRegion(region, animated: true)
//self.map.moveCenterByOffSet(offSet: CGPoint(x: 0, y: -130), coordinate: coordinate)
self.map.moveCenterByOffSet(offSet: CGPoint(x: 0, y: SCREEN_HEIGHT - SCREEN_HEIGHT * 4/3 + 0 ), coordinate: coordinate)
}
I have an app which requires the user's location to be constantly updated so I can display their current coordinates and altitude. I'm doing this using the didUpdateLocations function:
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
mapView.camera = GMSCameraPosition(target: locationManager.location!.coordinate, zoom: 15, bearing: 0, viewingAngle: 0)
let locValue : CLLocationCoordinate2D = manager.location!.coordinate
let altitude : CLLocationDistance = Double(round(1000*manager.location!.altitude)/1000)
let long = Double(round(10000*locValue.longitude)/10000)
let lat = Double(round(10000*locValue.latitude)/10000)
let alt = String(altitude) + " m"
latitudeLabel.text = String(lat)
longitudeLabel.text = String(long)
altitudeLabel.text = alt
showLearningObjectsWithinRange(location)
}
}
The problem is, when I try to zoom in on a certain spot on the map, if I move the device even slightly the camera zooms back out again. Obviously this is because of the first line in my didUpdateLocations function setting the camera position, but if I remove that line, the map doesn't center to their location at all.
I tried moving the GMSCameraPosition code to the viewDidLoad, viewWillAppear, and several other places, but this caused the app to crash because it couldn't locate the user in time.
Does anyone have any suggestions on how to make this work? Thanks.
use this instead of that certain line (mapview.camera = ...)
mapView.animate(toLocation: CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude))
With regards to implementing location update, there was an issue posted in GitHub - Implement Responsive User Location Tracking Mode due to location update optimization and going through the thread, a given workaround to show the user location on the map is to call
nMapView.setMyLocationEnabled(true);
instead of:
nMapView.setMyLocationTrackingMode(MyLocationTrackingMode.TRACKING_FOLLOW);
Then, with regards to camera zoom, as discussed in Camera and View - Zoom, you can try setting a minimum or maximum zoom to restrict zoom level. As stated,
You can restrict the range of zoom available to the map by setting a min and max zoom level.
You may also try the solutions given in this SO post - Google Maps iOS SDK, Getting Current Location of user. Hope it helps.
Easy to reproduce:
- create a new project
- put an MKMapView on the screen
- try to rotate it with 2 fingers
It rotates a little and stops, and when you release the fingers, it goes back to the original position.
How do I make it stay rotated?
And rotate as much as I want?
I'm using latest iOS (8.something), iPhone 6 simulator and Swift.
I figured out the problem.
Actually there is no solution, what was happening is that MKMapView does not allow you to stay rotated if the map region is too big.
If you zoom in you can rotate normally.
Please try this
Gloabally declare :
let regionRadius: CLLocationDistance = 1000
And in viewdidload:
let initialLocation = CLLocation(latitude: 21.282778, longitude: -157.829444)
centerMapOnLocation(initialLocation)
And then create a helper class:
func centerMapOnLocation(location: CLLocation) {
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,
regionRadius * 2.0, regionRadius * 2.0)
mapview.setRegion(coordinateRegion, animated: true)
}
Try with rotateEnabled property of MKMapView :
rotateEnabled -
A Boolean value indicating whether the map camera’s heading information is used.
Declaration
SWIFT
var rotateEnabled: Bool
When this property is set to YES and a valid camera is associated with the map, the camera’s heading angle is used to rotate the plane of the map around its center point. When this property is set to NO, the camera’s heading angle is ignored and the map is always oriented so that true north is situated at the top of the map view.
You have to override CLLocationManager.didUpdateLocations (part of CLLocationManagerDelegate) to get notified when the location manager retrieves the current location and don't do anything there:
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
// Don't do update the map to the new location
}