diff options
Diffstat (limited to 'platform/ios/app/MBXViewController.mm')
-rw-r--r-- | platform/ios/app/MBXViewController.mm | 669 |
1 files changed, 669 insertions, 0 deletions
diff --git a/platform/ios/app/MBXViewController.mm b/platform/ios/app/MBXViewController.mm new file mode 100644 index 0000000000..af4e425841 --- /dev/null +++ b/platform/ios/app/MBXViewController.mm @@ -0,0 +1,669 @@ +#import "MBXViewController.h" +#import "MBXCustomCalloutView.h" + +#import <Mapbox/Mapbox.h> +#import "../../../include/mbgl/util/default_styles.hpp" + +#import <CoreLocation/CoreLocation.h> +#import <OpenGLES/ES2/gl.h> + +static UIColor *const kTintColor = [UIColor colorWithRed:0.120 green:0.550 blue:0.670 alpha:1.000]; +static NSString * const kCustomCalloutTitle = @"Custom Callout"; + +static const CLLocationCoordinate2D WorldTourDestinations[] = { + { 38.9131982, -77.0325453144239 }, + { 37.7757368, -122.4135302 }, + { 12.9810816, 77.6368034 }, + { -13.15589555, -74.2178961777998 }, +}; + +@interface MBXViewController () <UIActionSheetDelegate, MGLMapViewDelegate> + +@property (nonatomic) MGLMapView *mapView; +@property (nonatomic) NSUInteger styleIndex; + +@end + +@implementation MBXViewController +{ + BOOL _isTouringWorld; + BOOL _isShowingCustomStyleLayer; +} + +#pragma mark - Setup + ++ (void)initialize +{ + if (self == [MBXViewController class]) + { + [[NSUserDefaults standardUserDefaults] registerDefaults:@{ + @"MBXUserTrackingMode": @(MGLUserTrackingModeNone), + @"MBXShowsUserLocation": @NO, + @"MBXDebug": @NO, + }]; + } +} + +- (id)init +{ + self = [super init]; + + if (self) + { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveState:) name:UIApplicationDidEnterBackgroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(restoreState:) name:UIApplicationWillEnterForegroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveState:) name:UIApplicationWillTerminateNotification object:nil]; + } + + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.mapView = [[MGLMapView alloc] initWithFrame:self.view.bounds]; + self.mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + self.mapView.delegate = self; + [self.view addSubview:self.mapView]; + + self.view.tintColor = kTintColor; + self.navigationController.navigationBar.tintColor = kTintColor; + self.mapView.tintColor = kTintColor; + + self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"settings.png"] + style:UIBarButtonItemStylePlain + target:self + action:@selector(showSettings)]; + + self.styleIndex = 0; + + UIButton *titleButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [titleButton setFrame:CGRectMake(0, 0, 150, 40)]; + [titleButton setTitle:@(mbgl::util::default_styles::orderedStyles[self.styleIndex].name) forState:UIControlStateNormal]; + [titleButton setTitleColor:kTintColor forState:UIControlStateNormal]; + [titleButton addTarget:self action:@selector(cycleStyles) forControlEvents:UIControlEventTouchUpInside]; + self.navigationItem.titleView = titleButton; + + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"TrackingLocationOffMask.png"] + style:UIBarButtonItemStylePlain + target:self + action:@selector(locateUser)]; + + [self.mapView addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)]]; + + [self restoreState:nil]; +} + +- (void)saveState:(__unused NSNotification *)notification +{ + if (self.mapView) + { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSData *archivedCamera = [NSKeyedArchiver archivedDataWithRootObject:self.mapView.camera]; + [defaults setObject:archivedCamera forKey:@"MBXCamera"]; + [defaults setInteger:self.mapView.userTrackingMode forKey:@"MBXUserTrackingMode"]; + [defaults setBool:self.mapView.showsUserLocation forKey:@"MBXShowsUserLocation"]; + [defaults setInteger:self.mapView.debugMask forKey:@"MBXDebugMask"]; + [defaults synchronize]; + } +} + +- (void)restoreState:(__unused NSNotification *)notification +{ + if (self.mapView) { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSData *archivedCamera = [defaults objectForKey:@"MBXCamera"]; + MGLMapCamera *camera = archivedCamera ? [NSKeyedUnarchiver unarchiveObjectWithData:archivedCamera] : nil; + if (camera) + { + self.mapView.camera = camera; + } + NSInteger uncheckedTrackingMode = [defaults integerForKey:@"MBXUserTrackingMode"]; + if (uncheckedTrackingMode >= 0 && + (NSUInteger)uncheckedTrackingMode >= MGLUserTrackingModeNone && + (NSUInteger)uncheckedTrackingMode <= MGLUserTrackingModeFollowWithCourse) + { + self.mapView.userTrackingMode = (MGLUserTrackingMode)uncheckedTrackingMode; + } + self.mapView.showsUserLocation = [defaults boolForKey:@"MBXShowsUserLocation"]; + NSInteger uncheckedDebugMask = [defaults integerForKey:@"MBXDebugMask"]; + if (uncheckedDebugMask >= 0) + { + self.mapView.debugMask = (MGLMapDebugMaskOptions)uncheckedDebugMask; + } + } +} + +- (NSUInteger)supportedInterfaceOrientations +{ + return UIInterfaceOrientationMaskAll; +} + +#pragma mark - Actions + +- (void)showSettings +{ + MGLMapDebugMaskOptions debugMask = self.mapView.debugMask; + UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"Map Settings" + delegate:self + cancelButtonTitle:@"Cancel" + destructiveButtonTitle:nil + otherButtonTitles:@"Reset Position", + ((debugMask & MGLMapDebugTileBoundariesMask) + ? @"Hide Tile Boundaries" + : @"Show Tile Boundaries"), + ((debugMask & MGLMapDebugTileInfoMask) + ? @"Hide Tile Info" + : @"Show Tile Info"), + ((debugMask & MGLMapDebugTimestampsMask) + ? @"Hide Tile Timestamps" + : @"Show Tile Timestamps"), + ((debugMask & MGLMapDebugCollisionBoxesMask) + ? @"Hide Collision Boxes" + : @"Show Collision Boxes"), + @"Empty Memory", + @"Add 100 Points", + @"Add 1,000 Points", + @"Add 10,000 Points", + @"Add Test Shapes", + @"Start World Tour", + @"Add Custom Callout Point", + @"Remove Annotations", + (_isShowingCustomStyleLayer + ? @"Hide Custom Style Layer" + : @"Show Custom Style Layer"), + @"Print Telemetry Logfile", + @"Delete Telemetry Logfile", + nil]; + + [sheet showFromBarButtonItem:self.navigationItem.leftBarButtonItem animated:YES]; +} + +- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex +{ + if (buttonIndex == actionSheet.firstOtherButtonIndex) + { + [self.mapView resetPosition]; + } + else if (buttonIndex == actionSheet.firstOtherButtonIndex + 1) + { + self.mapView.debugMask ^= MGLMapDebugTileBoundariesMask; + } + else if (buttonIndex == actionSheet.firstOtherButtonIndex + 2) + { + self.mapView.debugMask ^= MGLMapDebugTileInfoMask; + } + else if (buttonIndex == actionSheet.firstOtherButtonIndex + 3) + { + self.mapView.debugMask ^= MGLMapDebugTimestampsMask; + } + else if (buttonIndex == actionSheet.firstOtherButtonIndex + 4) + { + self.mapView.debugMask ^= MGLMapDebugCollisionBoxesMask; + } + else if (buttonIndex == actionSheet.firstOtherButtonIndex + 5) + { + [self.mapView emptyMemoryCache]; + } + else if (buttonIndex == actionSheet.firstOtherButtonIndex + 6) + { + [self parseFeaturesAddingCount:100]; + } + else if (buttonIndex == actionSheet.firstOtherButtonIndex + 7) + { + [self parseFeaturesAddingCount:1000]; + } + else if (buttonIndex == actionSheet.firstOtherButtonIndex + 8) + { + [self parseFeaturesAddingCount:10000]; + } + else if (buttonIndex == actionSheet.firstOtherButtonIndex + 9) + { + // PNW triangle + // + CLLocationCoordinate2D triangleCoordinates[3] = + { + CLLocationCoordinate2DMake(44, -122), + CLLocationCoordinate2DMake(46, -122), + CLLocationCoordinate2DMake(46, -121) + }; + + MGLPolygon *triangle = [MGLPolygon polygonWithCoordinates:triangleCoordinates count:3]; + + [self.mapView addAnnotation:triangle]; + + // Orcas Island hike + // + NSDictionary *hike = [NSJSONSerialization JSONObjectWithData: + [NSData dataWithContentsOfFile: + [[NSBundle mainBundle] pathForResource:@"polyline" ofType:@"geojson"]] + options:0 + error:nil]; + + NSArray *hikeCoordinatePairs = hike[@"features"][0][@"geometry"][@"coordinates"]; + + CLLocationCoordinate2D *polylineCoordinates = (CLLocationCoordinate2D *)malloc([hikeCoordinatePairs count] * sizeof(CLLocationCoordinate2D)); + + for (NSUInteger i = 0; i < [hikeCoordinatePairs count]; i++) + { + polylineCoordinates[i] = CLLocationCoordinate2DMake([hikeCoordinatePairs[i][1] doubleValue], [hikeCoordinatePairs[i][0] doubleValue]); + } + + MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:polylineCoordinates + count:[hikeCoordinatePairs count]]; + + [self.mapView addAnnotation:polyline]; + + free(polylineCoordinates); + + // PA/NJ/DE polys + // + NSDictionary *threestates = [NSJSONSerialization JSONObjectWithData: + [NSData dataWithContentsOfFile: + [[NSBundle mainBundle] pathForResource:@"threestates" ofType:@"geojson"]] + options:0 + error:nil]; + + for (NSDictionary *feature in threestates[@"features"]) + { + NSArray *stateCoordinatePairs = feature[@"geometry"][@"coordinates"]; + + while ([stateCoordinatePairs count] == 1) stateCoordinatePairs = stateCoordinatePairs[0]; + + CLLocationCoordinate2D *polygonCoordinates = (CLLocationCoordinate2D *)malloc([stateCoordinatePairs count] * sizeof(CLLocationCoordinate2D)); + + for (NSUInteger i = 0; i < [stateCoordinatePairs count]; i++) + { + polygonCoordinates[i] = CLLocationCoordinate2DMake([stateCoordinatePairs[i][1] doubleValue], [stateCoordinatePairs[i][0] doubleValue]); + } + + MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:polygonCoordinates count:[stateCoordinatePairs count]]; + + [self.mapView addAnnotation:polygon]; + + free(polygonCoordinates); + } + } + else if (buttonIndex == actionSheet.firstOtherButtonIndex + 10) + { + [self startWorldTour:actionSheet]; + } + else if (buttonIndex == actionSheet.firstOtherButtonIndex + 11) + { + [self presentAnnotationWithCustomCallout]; + } + else if (buttonIndex == actionSheet.firstOtherButtonIndex + 12) + { + [self.mapView removeAnnotations:self.mapView.annotations]; + } + else if (buttonIndex == actionSheet.firstOtherButtonIndex + 13) + { + if (_isShowingCustomStyleLayer) + { + [self removeCustomStyleLayer]; + } + else + { + [self insertCustomStyleLayer]; + } + } + else if (buttonIndex == actionSheet.firstOtherButtonIndex + 14) + { + NSString *fileContents = [NSString stringWithContentsOfFile:[self telemetryDebugLogfilePath] encoding:NSUTF8StringEncoding error:nil]; + NSLog(@"%@", fileContents); + } + else if (buttonIndex == actionSheet.firstOtherButtonIndex + 15) + { + NSString *filePath = [self telemetryDebugLogfilePath]; + if ([[NSFileManager defaultManager] isDeletableFileAtPath:filePath]) { + NSError *error; + BOOL success = [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error]; + if (success) { + NSLog(@"Deleted telemetry log."); + } else { + NSLog(@"Error deleting telemetry log: %@", error.localizedDescription); + } + } + } +} + +- (void)parseFeaturesAddingCount:(NSUInteger)featuresCount +{ + [self.mapView removeAnnotations:self.mapView.annotations]; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ + { + NSData *featuresData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"points" ofType:@"geojson"]]; + + id features = [NSJSONSerialization JSONObjectWithData:featuresData + options:0 + error:nil]; + + if ([features isKindOfClass:[NSDictionary class]]) + { + NSMutableArray *annotations = [NSMutableArray array]; + + for (NSDictionary *feature in features[@"features"]) + { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake([feature[@"geometry"][@"coordinates"][1] doubleValue], + [feature[@"geometry"][@"coordinates"][0] doubleValue]); + NSString *title = feature[@"properties"][@"NAME"]; + + MGLPointAnnotation *annotation = [MGLPointAnnotation new]; + annotation.coordinate = coordinate; + annotation.title = title; + + [annotations addObject:annotation]; + + if (annotations.count == featuresCount) break; + } + + dispatch_async(dispatch_get_main_queue(), ^ + { + [self.mapView addAnnotations:annotations]; + [self.mapView showAnnotations:annotations animated:YES]; + }); + } + }); +} + +- (void)insertCustomStyleLayer +{ + _isShowingCustomStyleLayer = YES; + + static const GLchar *vertexShaderSource = "attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0, 1); }"; + static const GLchar *fragmentShaderSource = "void main() { gl_FragColor = vec4(0, 1, 0, 1); }"; + + __block GLuint program = 0; + __block GLuint vertexShader = 0; + __block GLuint fragmentShader = 0; + __block GLuint buffer = 0; + __block GLuint a_pos = 0; + [self.mapView insertCustomStyleLayerWithIdentifier:@"mbx-custom" preparationHandler:^{ + program = glCreateProgram(); + vertexShader = glCreateShader(GL_VERTEX_SHADER); + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + + glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr); + glCompileShader(vertexShader); + glAttachShader(program, vertexShader); + glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr); + glCompileShader(fragmentShader); + glAttachShader(program, fragmentShader); + glLinkProgram(program); + a_pos = glGetAttribLocation(program, "a_pos"); + + GLfloat background[] = { -1,-1, 1,-1, -1,1, 1,1 }; + glGenBuffers(1, &buffer); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), background, GL_STATIC_DRAW); + } drawingHandler:^(__unused CGSize size, + __unused CLLocationCoordinate2D centerCoordinate, + __unused double zoomLevel, + __unused CLLocationDirection direction, + __unused CGFloat pitch, + __unused CGFloat perspectiveSkew) { + glUseProgram(program); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glEnableVertexAttribArray(a_pos); + glVertexAttribPointer(a_pos, 2, GL_FLOAT, GL_FALSE, 0, NULL); + glDisable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } completionHandler:^{ + if (program) { + glDeleteBuffers(1, &buffer); + glDetachShader(program, vertexShader); + glDetachShader(program, fragmentShader); + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + glDeleteProgram(program); + } + } belowStyleLayerWithIdentifier:@"housenum-label"]; +} + +- (void)removeCustomStyleLayer +{ + _isShowingCustomStyleLayer = NO; + [self.mapView removeCustomStyleLayerWithIdentifier:@"mbx-custom"]; +} + +- (void)presentAnnotationWithCustomCallout +{ + [self.mapView removeAnnotations:self.mapView.annotations]; + + MGLPointAnnotation *annotation = [MGLPointAnnotation new]; + annotation.coordinate = CLLocationCoordinate2DMake(48.8533940, 2.3775439); + annotation.title = kCustomCalloutTitle; + + [self.mapView addAnnotation:annotation]; + [self.mapView showAnnotations:@[annotation] animated:YES]; +} + +- (void)handleLongPress:(UILongPressGestureRecognizer *)longPress +{ + if (longPress.state == UIGestureRecognizerStateBegan) + { + MGLPointAnnotation *point = [MGLPointAnnotation new]; + point.coordinate = [self.mapView convertPoint:[longPress locationInView:longPress.view] + toCoordinateFromView:self.mapView]; + point.title = @"Dropped Marker"; + point.subtitle = [NSString stringWithFormat:@"lat: %.3f, lon: %.3f", point.coordinate.latitude, point.coordinate.longitude]; + [self.mapView addAnnotation:point]; + [self.mapView selectAnnotation:point animated:YES]; + } +} + +- (void)cycleStyles +{ + UIButton *titleButton = (UIButton *)self.navigationItem.titleView; + + self.styleIndex = (self.styleIndex + 1) % mbgl::util::default_styles::numOrderedStyles; + + self.mapView.styleURL = [NSURL URLWithString:@(mbgl::util::default_styles::orderedStyles[self.styleIndex].url)]; + + [titleButton setTitle:@(mbgl::util::default_styles::orderedStyles[self.styleIndex].name) forState:UIControlStateNormal]; +} + +- (void)locateUser +{ + MGLUserTrackingMode nextMode; + switch (self.mapView.userTrackingMode) { + case MGLUserTrackingModeNone: + nextMode = MGLUserTrackingModeFollow; + break; + case MGLUserTrackingModeFollow: + nextMode = MGLUserTrackingModeFollowWithHeading; + break; + case MGLUserTrackingModeFollowWithHeading: + nextMode = MGLUserTrackingModeFollowWithCourse; + break; + case MGLUserTrackingModeFollowWithCourse: + nextMode = MGLUserTrackingModeNone; + break; + } + self.mapView.userTrackingMode = nextMode; +} + +- (IBAction)startWorldTour:(__unused id)sender +{ + _isTouringWorld = YES; + + [self.mapView removeAnnotations:self.mapView.annotations]; + NSUInteger numberOfAnnotations = sizeof(WorldTourDestinations) / sizeof(WorldTourDestinations[0]); + NSMutableArray *annotations = [NSMutableArray arrayWithCapacity:numberOfAnnotations]; + for (NSUInteger i = 0; i < numberOfAnnotations; i++) + { + MGLPointAnnotation *annotation = [[MGLPointAnnotation alloc] init]; + annotation.coordinate = WorldTourDestinations[i]; + [annotations addObject:annotation]; + } + [self.mapView addAnnotations:annotations]; + [self continueWorldTourWithRemainingAnnotations:annotations]; +} + +- (void)continueWorldTourWithRemainingAnnotations:(NS_MUTABLE_ARRAY_OF(MGLPointAnnotation *) *)annotations +{ + MGLPointAnnotation *nextAnnotation = annotations.firstObject; + if (!nextAnnotation || !_isTouringWorld) + { + _isTouringWorld = NO; + return; + } + + [annotations removeObjectAtIndex:0]; + MGLMapCamera *camera = [MGLMapCamera cameraLookingAtCenterCoordinate:nextAnnotation.coordinate + fromDistance:10 + pitch:arc4random_uniform(60) + heading:arc4random_uniform(360)]; + __weak MBXViewController *weakSelf = self; + [self.mapView flyToCamera:camera completionHandler:^{ + MBXViewController *strongSelf = weakSelf; + [strongSelf performSelector:@selector(continueWorldTourWithRemainingAnnotations:) + withObject:annotations + afterDelay:2]; + }]; +} + +- (NSString *)telemetryDebugLogfilePath +{ + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateFormat:@"yyyy'-'MM'-'dd"]; + [dateFormatter setTimeZone:[NSTimeZone systemTimeZone]]; + NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"telemetry_log-%@.json", [dateFormatter stringFromDate:[NSDate date]]]]; + + return filePath; +} + +#pragma mark - Destruction + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [self saveState:nil]; +} + +#pragma mark - MGLMapViewDelegate + +- (MGLAnnotationImage *)mapView:(MGLMapView * __nonnull)mapView imageForAnnotation:(id <MGLAnnotation> __nonnull)annotation +{ + if ([annotation.title isEqualToString:@"Dropped Marker"] + || [annotation.title isEqualToString:kCustomCalloutTitle]) + { + return nil; // use default marker + } + + NSString *title = [(MGLPointAnnotation *)annotation title]; + if (!title.length) return nil; + NSString *lastTwoCharacters = [title substringFromIndex:title.length - 2]; + + UIColor *color; + + // make every tenth annotation blue + if ([lastTwoCharacters hasSuffix:@"0"]) { + color = [UIColor blueColor]; + } else { + color = [UIColor redColor]; + } + + MGLAnnotationImage *image = [mapView dequeueReusableAnnotationImageWithIdentifier:lastTwoCharacters]; + + if ( ! image) + { + CGRect rect = CGRectMake(0, 0, 20, 15); + + UIGraphicsBeginImageContextWithOptions(rect.size, NO, [[UIScreen mainScreen] scale]); + + CGContextRef ctx = UIGraphicsGetCurrentContext(); + + CGContextSetFillColorWithColor(ctx, [[color colorWithAlphaComponent:0.75] CGColor]); + CGContextFillRect(ctx, rect); + + CGContextSetStrokeColorWithColor(ctx, [[UIColor blackColor] CGColor]); + CGContextStrokeRectWithWidth(ctx, rect, 2); + + NSAttributedString *drawString = [[NSAttributedString alloc] initWithString:lastTwoCharacters attributes:@{ + NSFontAttributeName: [UIFont fontWithName:@"Arial-BoldMT" size:12], + NSForegroundColorAttributeName: [UIColor whiteColor] }]; + CGSize stringSize = drawString.size; + CGRect stringRect = CGRectMake((rect.size.width - stringSize.width) / 2, + (rect.size.height - stringSize.height) / 2, + stringSize.width, + stringSize.height); + [drawString drawInRect:stringRect]; + + image = [MGLAnnotationImage annotationImageWithImage:UIGraphicsGetImageFromCurrentImageContext() reuseIdentifier:lastTwoCharacters]; + + // don't allow touches on blue annotations + if ([color isEqual:[UIColor blueColor]]) image.enabled = NO; + + UIGraphicsEndImageContext(); + } + + return image; +} + +- (BOOL)mapView:(__unused MGLMapView *)mapView annotationCanShowCallout:(__unused id <MGLAnnotation>)annotation +{ + return YES; +} + +- (CGFloat)mapView:(__unused MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation +{ + return ([annotation isKindOfClass:[MGLPolygon class]] ? 0.5 : 1.0); +} + +- (UIColor *)mapView:(__unused MGLMapView *)mapView strokeColorForShapeAnnotation:(MGLShape *)annotation +{ + return ([annotation isKindOfClass:[MGLPolyline class]] ? [UIColor purpleColor] : [UIColor blackColor]); +} + +- (UIColor *)mapView:(__unused MGLMapView *)mapView fillColorForPolygonAnnotation:(__unused MGLPolygon *)annotation +{ + return (annotation.pointCount > 3 ? [UIColor greenColor] : [UIColor redColor]); +} + +- (void)mapView:(__unused MGLMapView *)mapView didChangeUserTrackingMode:(MGLUserTrackingMode)mode animated:(__unused BOOL)animated +{ + UIImage *newButtonImage; + NSString *newButtonTitle; + + switch (mode) { + case MGLUserTrackingModeNone: + newButtonImage = [UIImage imageNamed:@"TrackingLocationOffMask.png"]; + break; + + case MGLUserTrackingModeFollow: + newButtonImage = [UIImage imageNamed:@"TrackingLocationMask.png"]; + break; + + case MGLUserTrackingModeFollowWithHeading: + newButtonImage = [UIImage imageNamed:@"TrackingHeadingMask.png"]; + break; + case MGLUserTrackingModeFollowWithCourse: + newButtonImage = nil; + newButtonTitle = @"Course"; + break; + } + + self.navigationItem.rightBarButtonItem.title = newButtonTitle; + [UIView animateWithDuration:0.25 animations:^{ + self.navigationItem.rightBarButtonItem.image = newButtonImage; + }]; +} + +- (UIView<MGLCalloutView> *)mapView:(__unused MGLMapView *)mapView calloutViewForAnnotation:(id<MGLAnnotation>)annotation +{ + if ([annotation respondsToSelector:@selector(title)] + && [annotation.title isEqualToString:kCustomCalloutTitle]) + { + MBXCustomCalloutView *calloutView = [[MBXCustomCalloutView alloc] init]; + calloutView.representedObject = annotation; + return calloutView; + } + return nil; +} + +@end |