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
}
Related
I am using google map in the application. When I do not rotate map everything works fine. But when I rotate map I am getting problem as shown in image. To solve that I need to get the google map currently rotated angle by the user. I need to get this so that I can place the marker back on the map at the same angle. Currently my map angle after rotation and my overlay seems to be different after placing plan image on map even when I am getting correct top left & bottom right corner coordinates.
My code
let topCoordinate = CLLocationCoordinate2D(latitude: topLattitude, longitude: topLongitude)//top
let bottomCoordinate = CLLocationCoordinate2D(latitude: bottomLattitude, longitude: bottomLongitude)//bottom
overlayBounds = GMSCoordinateBounds(coordinate: topCoordinate, coordinate:
bottomCoordinate)
let overlay = GMSGroundOverlay(bounds: overlayBounds, icon: planImage)
overlay.map = mapView
GMSMapViewDelegate
Bearing of the camera, in degrees clockwise from true north.
func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) {
print(position.bearing)
}
I have solved my problem by changing the overlay method as
let zoomLevel = (object["image_zoom_level"] as? NSString)?.doubleValue
let overlay = GMSGroundOverlay(position: centreCoordinate, icon: planImage, zoomLevel: CGFloat(zoomLevel!))
overlay.map = mapView
if let angle = (object["rotation_angle"] as? NSString)?.doubleValue{
overlay.bearing = angle
}
Instead of using Overlay bounds I have just used center coordinate and placed overlay image using image zoom level and rotation angle. By this, my image is placing in proper angle and at a proper place. This works fine if the Image size is correct as you want place and the center point is accurate and precise.
I have a MKMapView that I configure with:
static let STARTING_MAP_RANGE: Double = 1000 // meters
. . .
let region: MKCoordinateRegion = MKCoordinateRegionMakeWithDistance(location,
MapViewController.STARTING_MAP_RANGE,
MapViewController.STARTING_MAP_RANGE)
mapView.setRegion(region, animated: true)
I add an annotation for the current location and it all looks fine. When I add an annotation for other points in the visible region, the MKMapView zooms in to the minimum area needed to show all the annotations.
The weird thing is that I tried to figure out where this was happening by printing out the bottom left and top right latitude and longitude like this:
private func printMapRegion(caller: String)
{
let mapRect = mapView.visibleMapRect;
let bottomLeft = MKCoordinateForMapPoint(MKMapPointMake(mapRect.origin.x, MKMapRectGetMaxY(mapRect)))
let topRight = MKCoordinateForMapPoint(MKMapPointMake(MKMapRectGetMaxX(mapRect), mapRect.origin.y))
print("\(caller): (\(bottomLeft.latitude),\(bottomLeft.longitude)) -- (\(topRight.latitude),\(topRight.longitude))")
}
When I run this before and after setting the annotations, I get identical values, despite seeing the map zoom on the screen (both in the simulator and my iPhone).
I added a refresh button to reset the map. It works as far as zooming the map out, but it also reports that the bottom left and top right coordinates are the same before and after zooming.
Is there something wrong with my understanding of visibleMapRect?
I found that the reason for the zooming is that I was adding the annotations like this:
mapView.removeAnnotations(mapView.annotations)
mapView.addAnnotations(annotations)
mapView.showAnnotations(mapView.annotations, animated: true)
Removing the call to showAnnotations eliminated the problem. I'm still curious as to why visibleMapRect is reporting the same bounding coordinates after the visible zoom.
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 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.
I'd like to tilt the map at startup. (Same as the user does when scrolling up or down with 2 fingers)
Is this possible using Swift?
MKMapView Class Reference : http://goo.gl/djHXPn
Look at the camera property :
A camera object defines a point above the map’s surface from which to view the map. Applying a camera to a map has the effect of giving the map a 3D-like appearance. You can use a camera to rotate the map so that it is oriented to match the user’s heading or to apply a pitch angle to tilt the plane of the map.
Assigning a new camera to this property updates the map immediately and without animating the change. If you want to animate changes in camera position, use the setCamera:animated: method instead.
You must not set this property to nil. To restore the map to a flat appearance, apply a camera with a pitch angle of 0, which yields a camera looking straight down onto the map surface.
Try to create and set a camera (animated or not).
Edit :
I tried myself. Here is an example of how to use it :
let userCoordinate = CLLocationCoordinate2D(latitude: 58.592725, longitude: 16.185962)
let eyeCoordinate = CLLocationCoordinate2D(latitude: 58.571647, longitude: 16.234660)
let mapCamera = MKMapCamera(lookingAtCenterCoordinate: userCoordinate, fromEyeCoordinate: eyeCoordinate, eyeAltitude: 400.0)
let annotation = MKPointAnnotation()
annotation.setCoordinate(userCoordinate)
mapView.addAnnotation(annotation)
mapView.setCamera(mapCamera, animated: true)
You'll have to find your right eyeCoordinate depending on your location and tilt effect you want to have.
Swift 4
This is an easier way: you can set distance, pitch and heading:
let mapCamera = MKMapCamera(lookingAtCenter: userCoordinate, fromDistance: 10000, pitch: 65, heading: 0)
map.setCamera(mapCamera, animated: true)