Erik Carlson
Erik Carlson

Reputation: 381

how can I show compass/heading on mapkit mapview

on the iPhone 3GS in the "Maps" app you can click the icon which usually shows your position twice and the blue dot gains what looks like a beam from a headlamp, basically showing you the direction you are facing on the map and rotating the image accordingly.

Is this option available using MapKit MapView ?

I'm know that I can get my heading with something like

- (void)locationManager:(CLLocationManager *) manager didUpdateHeading:(CLHeading *) newHeading {

// If the accuracy is valid, process the event.

if (newHeading.headingAccuracy > 0) {

    CLLocationDirection theHeading = newHeading.magneticHeading;
    ...
}

}

but I don't know how to get that nice headlamp effect in Mapkit and there doesn't seem to be any documentation.

Any ideas?

Upvotes: 9

Views: 12077

Answers (5)

RVN
RVN

Reputation: 4177

I could think of one method, though I have not implemented but may help you a bit.

  1. Firstly you need an image with a pointer of your choice, this pointer must be pointing Upwards 90degree.
  2. Now in your MapKit off the Current Location Marker.
  3. In your didUpdateHeading use the x and y values to calculate the angle of the direction.
  4. Use this angle to rotate the image of the pointer
  5. Use this rotated image as a Annotation Pin in your map.
  6. You would require to update the position of the pointer frequently.

Please post your suggestions/changes for the above approach.

Upvotes: 2

JTing
JTing

Reputation: 651

Swift 3 implementation

mapView.userTrackingMode = MKUserTrackingMode.followWithHeading

Upvotes: 1

ScarletWitch
ScarletWitch

Reputation: 532

Adding user tracking mode also helps. I know I am late, but possibly a help to other developers like me :)

self.mapView.userTrackingMode = RMUserTrackingModeFollowWithHeading;

Upvotes: 6

myell0w
myell0w

Reputation: 2200

I found a solution:

I rotate the map using the available heading-information with

[mapView setTransform:CGAffineTransformMakeRotation(heading.magneticHeading * M_PI / -180.0)];

Therefore the "beam" always points to the top of the device. I now just display an ImageView on top of the map and change it's position in locationManager:didUpdateToLocation:fromLocation:

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
        // scroll to new location
    MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(newLocation.coordinate, 2000, 2000);
    [self.mapView setRegion:region animated:YES];

        // set position of "beam" to position of blue dot
    self.headingAngleView.center = [self.mapView convertCoordinate:newLocation.coordinate toPointToView:self.view];
        // slightly adjust position of beam
    self.headingAngleView.frameTop -= self.headingAngleView.frameHeight/2 + 8;
 }

Whereby frameTop and frameHeight are shortcuts for frame.origin.y and frame.size.height. It is not ideal and sometimes lacks a little bit when the dot changes it's position, but I'm happy with the solution b/c it works.

Have a look at my OpenSource framework MTLocation which does this all (and a lot of other cool Map-related stuff for you):

MTLocation

Upvotes: 4

Jared Egan
Jared Egan

Reputation: 1278

The rotating logic of the other answers are good, however relying on the location manager delegate methods won't result in a good solution. The "beam" image will rarely be in the same place as the blue dot and will bounce around a lot.

The best solution is to get a reference to the blue dot view, and add the "beam" image as a subview to it. I went with a beam image that was a square, with the beam up top and transparency all around it. Imagine the blue dot being in the center of the image.


// An MKMapViewDelegate method. Use this to get a reference to the blue dot annotation view as soon as it gets added
- (void)mapView:(MKMapView *)aMapView didAddAnnotationViews:(NSArray *)views {

    for (MKAnnotationView *annotationView in views) {

        // Get the user location view, check for all of your custom annotation view classes here
        if (![annotationView isKindOfClass:[MKPinAnnotationView class]])
        {
            self.userLocationAnnotationView = annotationView;
            gotUsersLocationView = YES;
        }
    }
}


// Adds the 'viewPortView' to the annotation view. Assumes we have a reference to the annotation view. Call this before you start listening to for heading events.
- (void)addViewportToUserLocationAnnotationView {
    TTDASSERT(self.userLocationAnnotationView != nil);
    if (self.userLocationAnnotationView == nil) {
        // No reference to the view, can't do anything
        return;
    }

    if (self.viewPortView == nil) {
        self.viewPortView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"map_viewport.png"]] autorelease];
    }

    [self.userLocationAnnotationView addSubview:self.viewPortView];
    [self.userLocationAnnotationView sendSubviewToBack:self.viewPortView];

    self.viewPortView.frame = CGRectMake((-1 * self.viewPortView.frame.size.width/2) + self.userLocationAnnotationView.frame.size.width/2,
                                         (-1 * self.viewPortView.frame.size.height/2) + self.userLocationAnnotationView.frame.size.height/2,
                                         self.viewPortView.frame.size.width,
                                         self.viewPortView.frame.size.height);
}

Upvotes: 3

Related Questions