summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMinh Nguyễn <mxn@1ec5.org>2015-04-01 15:57:36 -0700
committerMinh Nguyễn <mxn@1ec5.org>2015-04-03 12:16:00 -0700
commitda13225a816ca40b8be209639c8ad0dc67681089 (patch)
tree229c4bebc244c6ecc396dd5931dc97db51eccb8b
parente18e4b0de5f601cdfb07eed209e64f88a114e492 (diff)
downloadqtlocation-mapboxgl-da13225a816ca40b8be209639c8ad0dc67681089.tar.gz
Made MGLMapView designable in IB
When an access token is set in the Attributes inspector (or as a user-defined runtime attribute), we draw some lovely Mapbox branding so the view shows up. (Manipulating invisible rectangles is a frustrating exercise, I’m told.) In the absence of an access token, the view displays a helpful message with directions for obtaining and setting the access token. Along the way, completely opt out of `MGLMapboxEvents` when targeting Interface Builder, because touching Core Location throws an exception in that environment and it doesn’t make sense to record any metrics when designing on the Interface Builder canvas. Also, don’t start `mbgl::Map` at all (and don’t update it) because none of the runtime drawing code should ever be run in the designable. Normally these chunks of code would be excluded in IB using the TARGET_INTERFACE_BUILDER preprocessor macro. However, Mapbox GL is being packaged as a static library, so the macro is only evaluated when the library is prebuilt, even if the library eventually makes its way into the CocoaPods-generated framework. Instead, we detect that we’re being run by the IBDesignablesAgentCocoaTouch process. Overrode `-[MGLMapView initWithFrame:]` to call `-commonInit`. We’ve marked this initializer unavailable in the header, but IB still calls it regardless. Fixes #929.
-rw-r--r--gyp/platform-ios.gypi2
-rw-r--r--include/mbgl/ios/MGLMapView.h11
-rw-r--r--platform/ios/MGLMapView.mm175
-rw-r--r--platform/ios/MGLMapboxEvents.m24
-rw-r--r--platform/ios/NSProcessInfo+MGLAdditions.h7
-rw-r--r--platform/ios/NSProcessInfo+MGLAdditions.m10
6 files changed, 199 insertions, 30 deletions
diff --git a/gyp/platform-ios.gypi b/gyp/platform-ios.gypi
index fc6771d83a..998a0d1b8f 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 dcf1313a8c..4139ef51cc 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://<user.style>`).
* @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<MGLMapViewDelegate> delegate;
+@property(nonatomic, weak) IBOutlet id<MGLMapViewDelegate> 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 67a62680e8..b1a2eeadbb 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 @@ static dispatch_once_t loadGLExtensions;
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 @@ static NSURL *MGLURLForBundledStyleNamed(NSString *styleName)
@end
-@implementation MGLMapView
+@implementation MGLMapView {
+ BOOL _isTargetingInterfaceBuilder;
+}
#pragma mark - Setup & Teardown -
@@ -106,24 +110,22 @@ MBGLView *mbglView = nullptr;
mbgl::SQLiteCache *mbglFileCache = nullptr;
mbgl::DefaultFileSource *mbglFileSource = nullptr;
-- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken
+- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self && [self commonInit])
{
- [self setAccessToken:accessToken];
-
- if (accessToken)
- {
- // If style is set directly, pass it on. If not, if we have an access
- // token, we can pass nil and use the default style.
- //
- self.styleURL = nil;
- }
+ self.styleURL = nil;
+ return self;
}
- return self;
+ return nil;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken
+{
+ return [self initWithFrame:frame accessToken:accessToken styleURL:nil];
}
- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken styleURL:(NSURL *)styleURL
@@ -175,6 +177,11 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)setStyleURL:(NSURL *)styleURL
{
+ if ( _isTargetingInterfaceBuilder )
+ {
+ return;
+ }
+
if ( ! styleURL)
{
styleURL = MGLURLForBundledStyleNamed([NSString stringWithFormat:@"%@-v%@",
@@ -193,6 +200,8 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (BOOL)commonInit
{
+ _isTargetingInterfaceBuilder = NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent;
+
// create context
//
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
@@ -362,8 +371,11 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
_regionChangeDelegateQueue = [NSOperationQueue new];
_regionChangeDelegateQueue.maxConcurrentOperationCount = 1;
- // start the main loop
- mbglMap->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 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)layoutSubviews
{
[super layoutSubviews];
- mbglMap->triggerUpdate();
+
+ if ( ! _isTargetingInterfaceBuilder)
+ {
+ mbglMap->triggerUpdate();
+ }
}
#pragma mark - Life Cycle -
@@ -2186,6 +2202,135 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
[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 1db0f9e9f6..d21a723d94 100644
--- a/platform/ios/MGLMapboxEvents.m
+++ b/platform/ios/MGLMapboxEvents.m
@@ -6,6 +6,7 @@
#import <CoreTelephony/CTCarrier.h>
#import "MGLMetricsLocationManager.h"
+#import "NSProcessInfo+MGLAdditions.h"
#include <sys/sysctl.h>
@@ -176,17 +177,20 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
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 0000000000..a117f3b76d
--- /dev/null
+++ b/platform/ios/NSProcessInfo+MGLAdditions.h
@@ -0,0 +1,7 @@
+#import <Foundation/Foundation.h>
+
+@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 0000000000..fca183c1f7
--- /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