diff --git a/gyp/platform-ios.gypi b/gyp/platform-ios.gypi index fc6771d83a4..998a0d1b8ff 100644 --- a/gyp/platform-ios.gypi +++ b/gyp/platform-ios.gypi @@ -30,6 +30,8 @@ '../include/mbgl/ios/MGLMetricsLocationManager.h', '../platform/ios/MGLMetricsLocationManager.m', '../include/mbgl/ios/MGLTypes.h', + '../platform/ios/NSProcessInfo+MGLAdditions.h', + '../platform/ios/NSProcessInfo+MGLAdditions.m', '../platform/ios/NSString+MGLAdditions.h', '../platform/ios/NSString+MGLAdditions.m', '../platform/ios/vendor/SMCalloutView/SMCalloutView.h', diff --git a/include/mbgl/ios/MGLMapView.h b/include/mbgl/ios/MGLMapView.h index dcf1313a8cf..4139ef51cc8 100644 --- a/include/mbgl/ios/MGLMapView.h +++ b/include/mbgl/ios/MGLMapView.h @@ -13,20 +13,21 @@ * Use of MGLMapView requires a Mapbox API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/account/apps/). If you instantiate an MGLMapView from Interface Builder, rendering of the map won't begin until the accessToken property has been set. * * @warning Please note that you are responsible for getting permission to use the map data, and for ensuring your use adheres to the relevant terms of use. */ +IB_DESIGNABLE @interface MGLMapView : UIView #pragma mark - Initializing a Map View /** @name Initializing a Map View */ -/** Initialize a map view with a given frame, style URL, and access token. +/** Initialize a map view with a given frame, access token, and style URL. * @param frame The frame with which to initialize the map view. * @param accessToken A Mapbox API access token. * @param styleURL The map style URL to use. Can be either an HTTP/HTTPS URL or a Mapbox map ID style URL (`mapbox://`). * @return An initialized map view, or `nil` if the map view was unable to be initialized. */ - (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken styleURL:(NSURL *)styleURL; -/** Initialize a map view with a given frame, the default style, and an access token. +/** Initialize a map view with the default style given a frame and access token. * @param frame The frame with which to initialize the map view. * @param accessToken A Mapbox API access token. * @return An initialized map view, or `nil` if the map view was unable to be initialized. */ @@ -49,7 +50,7 @@ /** A view controller whose top and bottom layout guides to use for proper setup of constraints in the map view internals. * * Certain components of the map view, such as the heading compass and the data attribution button, need to be aware of the view controller layout in order to avoid positioning content under a top navigation bar or a bottom toolbar. */ -@property (nonatomic, weak) UIViewController *viewControllerForLayoutGuides; +@property (nonatomic, weak) IBOutlet UIViewController *viewControllerForLayoutGuides; #pragma mark - Accessing Map Properties @@ -81,7 +82,7 @@ /** @name Accessing the Delegate */ // TODO -@property(nonatomic, weak) id delegate; +@property(nonatomic, weak) IBOutlet id delegate; #pragma mark - Manipulating the Visible Portion of the Map @@ -134,7 +135,7 @@ - (void)setDirection:(CLLocationDirection)direction animated:(BOOL)animated; /** Resets the map rotation to a northern heading. */ -- (void)resetNorth; +- (IBAction)resetNorth; #pragma mark - Converting Map Coordinates diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index 67a62680e89..b1a2eeadbb9 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -16,6 +16,7 @@ #import "MGLTypes.h" #import "NSString+MGLAdditions.h" +#import "NSProcessInfo+MGLAdditions.h" #import "MGLAnnotation.h" #import "MGLUserLocationAnnotationView.h" #import "MGLUserLocation_Private.h" @@ -46,6 +47,7 @@ NSString *const MGLDefaultStyleName = @"Emerald"; NSString *const MGLStyleVersion = @"7"; NSString *const MGLDefaultStyleMarkerSymbolName = @"default_marker"; +NSString *const MGLMapboxAccessTokenManagerURLDisplayString = @"mapbox.com/account/apps"; const NSTimeInterval MGLAnimationDuration = 0.3; const CGSize MGLAnnotationUpdateViewportOutset = {150, 150}; @@ -88,7 +90,9 @@ @interface MGLMapView () start(); + // start the main loop, but not on the IB canvas + if ( ! _isTargetingInterfaceBuilder) + { + mbglMap->start(); + } // metrics: map load event const mbgl::LatLng latLng = mbglMap->getLatLng(); @@ -550,7 +562,11 @@ - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect - (void)layoutSubviews { [super layoutSubviews]; - mbglMap->triggerUpdate(); + + if ( ! _isTargetingInterfaceBuilder) + { + mbglMap->triggerUpdate(); + } } #pragma mark - Life Cycle - @@ -2186,6 +2202,135 @@ - (void)invalidate [self notifyMapChange:@(mbgl::MapChangeRegionIsChanging)]; } +- (void)prepareForInterfaceBuilder +{ + [super prepareForInterfaceBuilder]; + + self.layer.borderColor = [UIColor colorWithWhite:184/255. alpha:1].CGColor; + self.layer.borderWidth = 1; + + if (self.accessToken) + { + self.layer.backgroundColor = [UIColor colorWithRed:59/255. + green:178/255. + blue:208/255. + alpha:0.8].CGColor; + + UIImage *image = [[self class] resourceImageNamed:@"mapbox.png"]; + UIImageView *previewView = [[UIImageView alloc] initWithImage:image]; + previewView.translatesAutoresizingMaskIntoConstraints = NO; + [self addSubview:previewView]; + [self addConstraint: + [NSLayoutConstraint constraintWithItem:previewView + attribute:NSLayoutAttributeCenterXWithinMargins + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeCenterXWithinMargins + multiplier:1 + constant:0]]; + [self addConstraint: + [NSLayoutConstraint constraintWithItem:previewView + attribute:NSLayoutAttributeCenterYWithinMargins + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeCenterYWithinMargins + multiplier:1 + constant:0]]; + } + else + { + UIView *diagnosticView = [[UIView alloc] init]; + diagnosticView.translatesAutoresizingMaskIntoConstraints = NO; + [self addSubview:diagnosticView]; + + // Headline + UILabel *headlineLabel = [[UILabel alloc] init]; + headlineLabel.text = @"No Access Token"; + headlineLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]; + headlineLabel.textAlignment = NSTextAlignmentCenter; + headlineLabel.numberOfLines = 1; + headlineLabel.translatesAutoresizingMaskIntoConstraints = NO; + [headlineLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow + forAxis:UILayoutConstraintAxisHorizontal]; + [diagnosticView addSubview:headlineLabel]; + + // Explanation + UILabel *explanationLabel = [[UILabel alloc] init]; + explanationLabel.text = @"To display a map here, you must provide a Mapbox access token. Get an access token from:"; + explanationLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody]; + explanationLabel.numberOfLines = 0; + explanationLabel.translatesAutoresizingMaskIntoConstraints = NO; + [explanationLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow + forAxis:UILayoutConstraintAxisHorizontal]; + [diagnosticView addSubview:explanationLabel]; + + // Link + UIButton *linkButton = [UIButton buttonWithType:UIButtonTypeSystem]; + [linkButton setTitle:MGLMapboxAccessTokenManagerURLDisplayString forState:UIControlStateNormal]; + linkButton.translatesAutoresizingMaskIntoConstraints = NO; + [linkButton setContentCompressionResistancePriority:UILayoutPriorityDefaultLow + forAxis:UILayoutConstraintAxisHorizontal]; + [diagnosticView addSubview:linkButton]; + + // More explanation + UILabel *explanationLabel2 = [[UILabel alloc] init]; + explanationLabel2.text = @"and enter it into the Access Token field in the Attributes inspector."; + explanationLabel2.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody]; + explanationLabel2.numberOfLines = 0; + explanationLabel2.translatesAutoresizingMaskIntoConstraints = NO; + [explanationLabel2 setContentCompressionResistancePriority:UILayoutPriorityDefaultLow + forAxis:UILayoutConstraintAxisHorizontal]; + [diagnosticView addSubview:explanationLabel2]; + + // Constraints + NSDictionary *views = @{ + @"container": diagnosticView, + @"headline": headlineLabel, + @"explanation": explanationLabel, + @"link": linkButton, + @"explanation2": explanationLabel2, + }; + [self addConstraint: + [NSLayoutConstraint constraintWithItem:diagnosticView + attribute:NSLayoutAttributeCenterYWithinMargins + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeCenterYWithinMargins + multiplier:1 + constant:0]]; + [self addConstraints: + [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[container(20@20)]-|" + options:NSLayoutFormatAlignAllCenterY + metrics:nil + views:views]]; + [self addConstraints: + [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[headline]-[explanation]-[link]-[explanation2]|" + options:0 + metrics:nil + views:views]]; + [self addConstraints: + [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[headline]|" + options:0 + metrics:nil + views:views]]; + [self addConstraints: + [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[explanation]|" + options:0 + metrics:nil + views:views]]; + [self addConstraints: + [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[link]|" + options:0 + metrics:nil + views:views]]; + [self addConstraints: + [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[explanation2]|" + options:0 + metrics:nil + views:views]]; + } +} + class MBGLView : public mbgl::View { public: diff --git a/platform/ios/MGLMapboxEvents.m b/platform/ios/MGLMapboxEvents.m index 1db0f9e9f61..d21a723d949 100644 --- a/platform/ios/MGLMapboxEvents.m +++ b/platform/ios/MGLMapboxEvents.m @@ -6,6 +6,7 @@ #import #import "MGLMetricsLocationManager.h" +#import "NSProcessInfo+MGLAdditions.h" #include @@ -176,17 +177,20 @@ + (instancetype)sharedManager { static dispatch_once_t onceToken; static MGLMapboxEvents *_sharedManager; dispatch_once(&onceToken, ^{ - void (^setupBlock)() = ^{ - _sharedManager = [[self alloc] init]; - // setup dedicated location manager on first use - [MGLMetricsLocationManager sharedManager]; - }; - if ( ! [[NSThread currentThread] isMainThread]) { - dispatch_sync(dispatch_get_main_queue(), ^{ + if ( ! NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent) { + void (^setupBlock)() = ^{ + _sharedManager = [[self alloc] init]; + // setup dedicated location manager on first use + [MGLMetricsLocationManager sharedManager]; + }; + if ( ! [[NSThread currentThread] isMainThread]) { + dispatch_sync(dispatch_get_main_queue(), ^{ + setupBlock(); + }); + } + else { setupBlock(); - }); - } else { - setupBlock(); + } } }); return _sharedManager; diff --git a/platform/ios/NSProcessInfo+MGLAdditions.h b/platform/ios/NSProcessInfo+MGLAdditions.h new file mode 100644 index 00000000000..a117f3b76d1 --- /dev/null +++ b/platform/ios/NSProcessInfo+MGLAdditions.h @@ -0,0 +1,7 @@ +#import + +@interface NSProcessInfo (MGLAdditions) + +- (BOOL)mgl_isInterfaceBuilderDesignablesAgent; + +@end diff --git a/platform/ios/NSProcessInfo+MGLAdditions.m b/platform/ios/NSProcessInfo+MGLAdditions.m new file mode 100644 index 00000000000..fca183c1f70 --- /dev/null +++ b/platform/ios/NSProcessInfo+MGLAdditions.m @@ -0,0 +1,10 @@ +#import "NSProcessInfo+MGLAdditions.h" + +@implementation NSProcessInfo (MGLAdditions) + +- (BOOL)mgl_isInterfaceBuilderDesignablesAgent +{ + return [self.processName isEqualToString:@"IBDesignablesAgentCocoaTouch"]; +} + +@end