Direction of target annotation when outside of visible area

跟風遠走 提交于 2019-12-11 01:55:34

问题


I would like to show the direction images on all sides of the screen. E.g. if the target's location is the right side of user's location and is outside of the visible map area, then I want to add a direction image as shown on the picture below (The Green annotation is user's location, red one is the direction for the target, which is out of bounds of the screen):

What is the standard approach to do this?


回答1:


The simplest way is to place four "pointer" views above the map at each of the cardinal points. Then, as the user moves the map (using mapView:regionDidChangeAnimated: delegate method) determine which pointer should be shown. Hide all the other ones; and then show the correct one. Also, apply a transformation to the pointer so that the bearing angle is represented as you have done.

Here is a screenshot of a storyboard with the above configuration:

And here is a sample implementation (Code is not optimal, of course.):

//
//  MapViewController.m
//  AnimationTest
//
//  Created by Scott Atkinson on 4/17/15.
//

#import "MapViewController.h"

@import MapKit;

typedef NS_ENUM(NSInteger, CardinalPoint) {
    North,
    South,
    East,
    West
};

@interface MapViewController () <MKMapViewDelegate>

@property (weak, nonatomic) IBOutlet MKMapView *mapView;

// Views that show cardinal points on the map (Only one should be shown at a time)
@property (weak, nonatomic) IBOutlet UIView *northPointerView;
@property (weak, nonatomic) IBOutlet UIView *eastPointerView;
@property (weak, nonatomic) IBOutlet UIView *westPointerView;
@property (weak, nonatomic) IBOutlet UIView *southPointerView;

// Location to show on the map
@property (strong, nonatomic) CLLocation * targetLocation;

@end

@implementation MapViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self hidePointerViews];

    // Add the location to the map
    self.targetLocation = [[CLLocation alloc] initWithLatitude:37.331898 longitude:-122.029824];
    MKPlacemark * placemark = [[MKPlacemark alloc] initWithCoordinate:self.targetLocation.coordinate addressDictionary:nil];
    [self.mapView addAnnotation:placemark];
}


// ******************** MKMapViewDelegate ********************
#pragma mark - MKMapViewDelegate

// As the map moves, update the cardinal pointer views
- (void) mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
    if([self isCurrentLocationVisible] && ![self isTargetLocationVisible]) {
        // The user location is visible, but the target is not, so show a pointer
        double bearing = [self bearingToLocation:self.targetLocation fromLocation:self.mapView.userLocation.location];
        [self showCardinalPointDirection:bearing];

    } else {
        // Hide the pointers
        [self hidePointerViews];
    }
}


// ******************** Coordinate Helpers ********************
#pragma mark - Coordinate Helpers

- (BOOL) isCurrentLocationVisible {
    return MKMapRectContainsPoint(self.mapView.visibleMapRect,
                                  MKMapPointForCoordinate(self.mapView.userLocation.coordinate));
}

- (BOOL) isTargetLocationVisible {
    return MKMapRectContainsPoint(self.mapView.visibleMapRect,
                                  MKMapPointForCoordinate(self.targetLocation.coordinate));
}

// From: http://stackoverflow.com/questions/3925942/cllocation-category-for-calculating-bearing-w-haversine-function
double DegreesToRadians(double degrees) {return degrees * M_PI / 180.0;};
double RadiansToDegrees(double radians) {return radians * 180.0/M_PI;};

/// Calculate the bearing between two points
-(double) bearingToLocation:(CLLocation *) destinationLocation fromLocation:(CLLocation *) fromLocation {

    double lat1 = DegreesToRadians(fromLocation.coordinate.latitude);
    double lon1 = DegreesToRadians(fromLocation.coordinate.longitude);

    double lat2 = DegreesToRadians(destinationLocation.coordinate.latitude);
    double lon2 = DegreesToRadians(destinationLocation.coordinate.longitude);

    double dLon = lon2 - lon1;

    double y = sin(dLon) * cos(lat2);
    double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon);
    double radiansBearing = atan2(y, x);

    if(radiansBearing < 0.0)
        radiansBearing += 2*M_PI;

    return RadiansToDegrees(radiansBearing);
}

// ******************** Pointer View ********************
#pragma mark - Pointer View

- (void) hidePointerViews {
    self.northPointerView.hidden =
    self.southPointerView.hidden =
    self.eastPointerView.hidden =
    self.westPointerView.hidden = YES;
}

- (void) showCardinalPointDirection:(double) bearing {
    CardinalPoint point = [self cardinalPointWithBearing:bearing];

    // Determine which pointer should be shown based on the bearing
    UIView * activePointer;
    switch (point) {
        case North:
            activePointer = self.northPointerView;
            break;
        case South:
            activePointer = self.southPointerView;
            break;
        case East:
            activePointer = self.eastPointerView;
            break;
        case West:
            activePointer = self.westPointerView;
            break;
    }

    // Rotate the pointer to show the bearing
    activePointer.transform = CGAffineTransformMakeRotation(DegreesToRadians(bearing));

    // Hide all pointers except the pertinent one
    [self hidePointerViews];
    activePointer.hidden = NO;
}

/// Returns the cardinal point for a given bearing (in Degrees)
- (CardinalPoint) cardinalPointWithBearing:(double) bearing {
    if (bearing > 45.0 && bearing <= 135.0) {
        return East;
    } else if (bearing > 135.0 && bearing <= 225.0) {
        return South;
    } else if (bearing > 225.0 && bearing <= 315.0) {
        return West;
    } else {
        return North;
    }
}
@end

Additionally, the pointer rotation is based on the bearing between the userLocation and the targetLocation. It feels a little strange. Probably better to make the rotation based off of some other point. Maybe the center of the visible region at that moment...



来源:https://stackoverflow.com/questions/29271692/direction-of-target-annotation-when-outside-of-visible-area

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!