I'm trying to customize the appearance of the layers of my annotations in mapbox.
I want to rotate every annotation layer.
- (RMMapLayer *)mapView:(RMMapView *)mapView layerForAnnotation:(RMAnnotation *)annotation
{
if (annotation.isUserLocationAnnotation)
return nil;
RMMarker *marker;
CGPoint xy = CGPointFromString(annotation.userInfo);
marker = [[RMMarker alloc] initWithUIImage:[UIImage imageNamed:#"arrow.png"]];
marker.transform = CATransform3DMakeRotation(atan2f(xy.x, xy.y) * 180 / M_PI,0,0,1);
marker.canShowCallout = YES;
return marker;
}
It works the first time they load on the map. But when I move or zoom the map they reset back to their initial transform identity matrix.
Any ideas how I could solve this ? Or is it a bug of mapbox ?
Take a look in RMMapView.m at the annotationTransform. This gets corrected during movements for things like compass tracking mode, so you'll want to modify things with your own custom transform(s) as this isn't a public API part of this SDK.
Related
I need to create a map view interface, which is something similar to the OLA Cabs Application in iOS. What I exactly wanna do is to fix an overlay on mapView and allow the user to scroll the map view across it. So that the overlay can be fixed at any location the User wants it to, I searched a lot about overlays, in iOS and MapKit, but couldn't make it possible. If some one can give me tips for achieving this I would be really grateful. Here is a snapshot of the screen
Here the annotation remains fixed and you can move the map view across it, So that when you stop the mapview, the overlay will be pointing to the new location, where you stopped
Click here to download demo...
Create a fix MKAnnotation and image view object to animating the location change effect in Map view.
#property (nonatomic, strong) CustomAnnotation *fixAnnotation;
#property (nonatomic, strong) UIImageView *annotationImage;
Add this code in viewDidLoad() method:
// Fix annotation
_fixAnnotation = [[CustomAnnotation alloc] initWithTitle:#"Fix annotation" subTitle:#"Location" detailURL:nil location:self.mapView.userLocation.coordinate];
[self.mapView addAnnotation:self.fixAnnotation];
// Annotation image.
CGFloat width = 64;
CGFloat height = 64;
CGFloat margiX = self.mapView.center.x - (width / 2);
CGFloat margiY = self.mapView.center.y - (height / 2) - 32;
// 32 is half size for navigationbar and status bar height to set exact location for image.
_annotationImage = [[UIImageView alloc] initWithFrame:CGRectMake(margiX, margiY, width, height)];
[self.annotationImage setImage:[UIImage imageNamed:#"mapannotation.png"]];
Now have to remove image when you drag a map view and add image which looks like an annotation. And after completion of that add annotation and remove image from Map View.
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
NSLog(#"Region will changed...");
[self.mapView removeAnnotation:self.fixAnnotation];
[self.mapView addSubview:self.annotationImage];
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
NSLog(#"Region did changed...");
[self.annotationImage removeFromSuperview];
CLLocationCoordinate2D centre = [mapView centerCoordinate];
self.fixAnnotation.coordinate = centre;
[self.mapView addAnnotation:self.fixAnnotation];
}
Its not an map annotation overlay, its a normal UIImageView which has been placed over MKMapView, and it always used to get the lat-long for the center point of the map.
Hope this would be an easy way to achieve your goal.
#Kampai has added the same code for you.
I am using a MKUserTrackingBarButtonItem button to allow the user to automatically track their location on a map. The problem is that when they tap this button, it is zoomed too far out. I want it to start at a specified zoom level (i.e. span). How can I achieve this?
When the user taps the button to change to MKUserTrackingModeFollow, it seems to use the same zoom level that the user last manually changed to (i.e. using gestures on the map). Attempting to specify a different zoom level via setRegion or setVisibleMapRect does not affect what zoom level will be used when the mode is changed to MKUserTrackingModeFollow.
Attempting to override mapView:didChangeUserTrackingMode: to set the region causes the mode to be changed back to MKUserTrackingModeNone. Example:
- (void)mapView:(MKMapView *)mapView didChangeUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated {
if (mode == MKUserTrackingModeFollow) {
CLLocationCoordinate2D center = mapView.userLocation.location.coordinate;
MKCoordinateSpan span = MKCoordinateSpanMake(0.002306, 0.001717);
[mapView setRegion:MKCoordinateRegionMake(center, span) animated:YES];
// [mapView setUserTrackingMode:MKUserTrackingModeFollow animated:NO];
}
}
If I attempt to reset the mode immediately after setting the region, it works fine if the user is stationary, but zooms back out if the user is moving.
The simplest solution would be if there was a way to simply specify something like a zoom level for MKUserTraking by sending it my span value. However, since that doesn't seem to exist, what else can I do?
I had the same issue and used a different approach to fix it. You can use the MapCamera function for this instead of that button.
On each new location do this:
MKMapCamera *newCamera = [MKMapCamera cameraLookingAtCenterCoordinate:[newLocation coordinate]
fromEyeCoordinate:[oldLocation coordinate]
eyeAltitude:2000];
[mapView setCamera:newCamera animated:TRUE];
And play with the eyeAltitude.
If the user manually zooms in or out you can read the altitude value from mapview.camera.altitude also don't update the camera when the user is manually using the map.
According to apple documentation used here
https://developer.apple.com/reference/mapkit/mkmapview/1616208-usertrackingmode
Setting the tracking mode to follow or followWithHeading causes the map view to center the map on that location and begin tracking the user’s location. If the map is zoomed out, the map view automatically zooms in on the user’s location, effectively changing the current visible region.
Here changing the region does not effect your visible region due to that reason.
- (void)mapView:(MKMapView *)mapView didChangeUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated {
if (mode == MKUserTrackingModeFollow) {
CLLocationCoordinate2D center = mapView.userLocation.location.coordinate;
MKCoordinateSpan span = MKCoordinateSpanMake(0.002306, 0.001717);
[mapView setRegion:MKCoordinateRegionMake(center, span) animated:YES];
// [mapView setUserTrackingMode:MKUserTrackingModeFollow animated:NO];
}
}
So you just need to change center coordinate on didChangeUserTrackingMode instead of changing the whole region
- (void)mapView:(MKMapView *)mapView didChangeUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated {
if (mode == MKUserTrackingModeFollow) {
[self.mapView setCenterCoordinate:mapView.userLocation.location.coordinate animated:YES];
}
}
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
[self.mapView setCenterCoordinate:mapViewuserLocation.location.coordinate animated:YES];
}
on click of MKUserTrackingBarButtonItem change the zoom level
CLLocationCoordinate2D center = mapView.userLocation.location.coordinate;
MKCoordinateSpan span = MKCoordinateSpanMake(0.002306, 0.001717);
[mapView setRegion:MKCoordinateRegionMake(center, span) animated:YES];
I have an array of coordinates and I already know how to add it on a map as annotations.
What I'd like to do now is the following:
each annotation should be a red circle (no pins) that represents a fixed radius of 1 Km around the coordinates. That means that if I zoom in or out the map, the circle should adjusts itself to always represent a 1 Km radius;
if two or more circles overlaps, their color intensity should increase. For example, three or four overlapping circles will produce a solid red circle.
That's all. I have no idea where to start with this, so any help will be greatly appreciated.
For starter you can use below code but you will have to tweak it little to make it of your use:
in .h file confirm to MKMapViewDelegate
#interface MapViewController : UIViewController <MKMapViewDelegate>
Then,
in "viewDidLoad"
CLLocationCoordinate2D center = {X cordinate, Y cordinate};
//--> Add overlay
MKCircle *mCircle = [MKCircle circleWithCenterCoordinate:center radius:1000]; //set radius as per your need
[self.mapView addOverlay:mCircle];
Then,
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
MKCircleView *cirView = [[MKCircleView alloc] initWithOverlay:overlay];
[cirView setFillColor:[UIColor redColor]];
[cirView setStrokeColor:[UIColor blackColor]];
[cirView setAlpha:0.3f];
return cirView;
}
I think this should get you started.
i am using MKMapView in my custom app and would like to show a map scale (tape measure) during zooming like Apple's Maps.app. Is this possible?
If not, and i would implement my own map scale, how can i get continious update information while the zoom ov the MKMapView is changed?
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
seems to be called only once at the begining of a zoom while
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
is called only once at the end of the zoom.
Maps.app map scale is shown and updated in realtime continiously during a zoom.
thanks in advance.
I had a similar problem, getting the camera.altitude based on user zooming, to display in a label.
Since there is no method such as "regionISChangingAnimated", but only WillChange and DidChange, I start a timer at WillChange and invalidate it at DidChange. The timer calls a method (updateElevationLabel) that calculates the altitude of the camera above the map.
However, since camera.altitude is not calculated until regionDidChange is called, use the zoomscale and the starting height of the map (zoomscale = 1.0 does not always equal altitude = 0m, it depends on where you are in the world) to calculate the current height. Starting height is a float in the method below, set it once on load and for each region change.
Finally you can change the format of the altitude, e.g. from km from m beyond a certain altitude (10'000 metres below).
For the oldschool: 1m = 3.2808399 ft.
-(void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
if (showsElevation) {
//update starting height for the region
MKMapCamera *camera = map.camera;
CLLocationDistance altitude = camera.altitude;
MKZoomScale currentZoomScale = map.bounds.size.width / map.visibleMapRect.size.width;
float factor = 1.0/currentZoomScale;
startingHeight = altitude/factor;
elevationTimer = [NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(updateElevationLabel)
userInfo:Nil
repeats:YES];
[elevationTimer fire];
}
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
[elevationTimer invalidate];
}
-(void)updateElevationLabel {
//1. create the label
if (!elevationLabel) {
elevationLabel = [UILabel new];
[elevationLabel setFrame:CGRectMake(0, 18, 200, 44)];
[elevationLabel setBackgroundColor:[UIColor redColor]];
[self addSubview:elevationLabel];
}
//2. grab the initial starting height (further updated on region changes)
if (startingHeight == 0) {
MKMapCamera *camera = map.camera;
CLLocationDistance altitude = camera.altitude;
MKZoomScale currentZoomScale = map.bounds.size.width / map.visibleMapRect.size.width;
float factor = 1.0/currentZoomScale;
startingHeight = altitude/factor;
}
//3. get current zoom scale and altitude, format changes
MKZoomScale currentZoomScale = map.bounds.size.width / map.visibleMapRect.size.width;
float altitude = startingHeight * (1/currentZoomScale);
if (altitude>10000) {
altitude = altitude/1000;
[elevationLabel setText:[NSString stringWithFormat:#"%.1fkm", altitude]];
} else {
[elevationLabel setText:[NSString stringWithFormat:#"%.0fm", altitude]];
}
}
From Apple's docs on MKMapView's regionWillChangeAnimated: method (emphasis mine):
This method is called whenever the currently displayed map region
changes. During scrolling, this method may be called many times to
report updates to the map position. Therefore, your implementation of
this method should be as lightweight as possible to avoid affecting
scrolling performance.
Sounds like you should be able to use this method continuously as the map view scrolls, which solves part of your problem - so when that method is called, take a look at (I'm guessing here) the mapview's region attribute and derive your map scale from that.
I have seen this question posed before and all responses appear largely similar. However, I am simply unable to get any of them to work for my project.
I have an action, updateMap, that is triggered by a button. It shows current location on map (MapKit), and shows some other info relative to their location (geocoding). All of that works fine. What I need to do though is show a radius around their location on the map. Here is what I'm trying that isn't working:
(in updateMap)
//display radius
CLLocationCoordinate2D center = {current.coordinate.latitude, current.coordinate.longitude};
MKCircle *circle = [MKCircle circleWithCenterCoordinate:center radius:1000];
[self.mapView addOverlay:circle];
("current" is a CLLocation var that has the user's current location)
Then, after the action in the implementation file I have this:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
MKCircleView *circleView = [[MKCircleView alloc] initWithOverlay:overlay];
[circleView setFillColor:[UIColor redColor]];
[circleView setStrokeColor:[UIColor blackColor]];
[circleView setAlpha:0.5f];
return circleView;
}
It builds fine, but no circle is ever shown on the map.
Other solutions on this site involved putting the first code shown in the viewDidLoad method, but since the location hasn't been calculated yet (only determined within above-mentioned action) that isn't an option for me.
I appreciate your time and any help you can provide. I'm by no means on the level of many on here so please excuse me if this has an obvious answer. I'm not wanting to be spoon fed, I'm wanting to learn! Thanks for reading.