summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMinh Nguyễn <mxn@1ec5.org>2015-04-03 13:22:35 -0700
committerMinh Nguyễn <mxn@1ec5.org>2015-04-03 13:22:35 -0700
commite6e340afd17a29ee4d183540e716376cdb1ef88e (patch)
tree8c8d1e11a84b2ef1ca92996bb8fa4c9c63704dfb
parent999ebb371e98bb9ee5876cfad336426c5abae7a8 (diff)
parentd1fb43e37182e6364f6f0efa3c2644709893159a (diff)
downloadqtlocation-mapboxgl-e6e340afd17a29ee4d183540e716376cdb1ef88e.tar.gz
Merge pull request #1184 from mapbox/1070-kvo
Style API cleanup, designables, inspectables
-rw-r--r--gyp/platform-ios.gypi6
-rw-r--r--include/mbgl/ios/MGLMapView+IBAdditions.h33
-rw-r--r--include/mbgl/ios/MGLMapView.h73
-rw-r--r--include/mbgl/map/map.hpp1
-rw-r--r--ios/app/MBXViewController.mm8
-rw-r--r--platform/ios/MGLMapView.mm436
-rw-r--r--platform/ios/MGLMapboxEvents.m24
-rw-r--r--platform/ios/NSProcessInfo+MGLAdditions.h7
-rw-r--r--platform/ios/NSProcessInfo+MGLAdditions.m10
-rw-r--r--platform/ios/NSString+MGLAdditions.h8
-rw-r--r--platform/ios/NSString+MGLAdditions.m10
-rw-r--r--src/mbgl/map/map.cpp6
12 files changed, 467 insertions, 155 deletions
diff --git a/gyp/platform-ios.gypi b/gyp/platform-ios.gypi
index 9965db57ac..f707431182 100644
--- a/gyp/platform-ios.gypi
+++ b/gyp/platform-ios.gypi
@@ -20,6 +20,7 @@
'../include/mbgl/ios/MGLMapboxEvents.h',
'../platform/ios/MGLMapboxEvents.m',
'../include/mbgl/ios/MGLMapView.h',
+ '../include/mbgl/ios/MGLMapView+IBAdditions.h',
'../platform/ios/MGLMapView.mm',
'../include/mbgl/ios/MGLAnnotation.h',
'../include/mbgl/ios/MGLUserLocation.h',
@@ -30,6 +31,10 @@
'../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',
'../platform/ios/vendor/SMCalloutView/SMCalloutView.m',
],
@@ -50,6 +55,7 @@
'-framework MobileCoreServices',
'-framework QuartzCore',
'-framework SystemConfiguration',
+ '-ObjC',
],
},
diff --git a/include/mbgl/ios/MGLMapView+IBAdditions.h b/include/mbgl/ios/MGLMapView+IBAdditions.h
new file mode 100644
index 0000000000..df9a19a6b7
--- /dev/null
+++ b/include/mbgl/ios/MGLMapView+IBAdditions.h
@@ -0,0 +1,33 @@
+#import "MGLMapView.h"
+
+@interface MGLMapView (IBAdditions)
+
+// Core properties that can be manipulated in the Attributes inspector in
+// Interface Builder. These redeclarations merely add the IBInspectable keyword.
+// They appear here to ensure that they appear above the convenience properties;
+// inspectables declared in MGLMapView.h are always sorted before those in
+// MGLMapView+IBAdditions.h, due to ASCII sort order.
+
+@property (nonatomic) IBInspectable NSString *accessToken;
+@property (nonatomic) IBInspectable NSString *mapID;
+
+// Convenience properties related to the initial viewport. These properties
+// are not meant to be used outside of Interface Builder. latitude and longitude
+// are backed by properties of type CLLocationDegrees, but these declarations
+// must use the type double because Interface Builder is unaware that
+// CLLocationDegrees is a typedef for double.
+
+@property (nonatomic) IBInspectable double latitude;
+@property (nonatomic) IBInspectable double longitude;
+@property (nonatomic) IBInspectable double zoomLevel;
+
+// Renamed properties. Interface Builder derives the display name of each
+// inspectable from the runtime name, but runtime names don’t always make sense
+// in UI.
+
+@property (nonatomic) IBInspectable BOOL allowsZooming;
+@property (nonatomic) IBInspectable BOOL allowsScrolling;
+@property (nonatomic) IBInspectable BOOL allowsRotating;
+@property (nonatomic) IBInspectable BOOL showsUserLocation;
+
+@end
diff --git a/include/mbgl/ios/MGLMapView.h b/include/mbgl/ios/MGLMapView.h
index 17741b1711..5db1f425d5 100644
--- a/include/mbgl/ios/MGLMapView.h
+++ b/include/mbgl/ios/MGLMapView.h
@@ -13,48 +13,34 @@
* 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, and access token.
+/** Initialize a map view with the default style and a given frame and access token.
* @param frame The frame with which to initialize the map view.
* @param accessToken A Mapbox API access token.
-* @param styleJSON The map stylesheet as JSON text.
* @return An initialized map view, or `nil` if the map view was unable to be initialized. */
-- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken styleJSON:(NSString *)styleJSON;
-
-/** Initialize a map view with a given frame, bundled style name, and access token.
-* @param frame The frame with which to initialize the map view.
-* @param accessToken A Mapbox API access token.
-* @param styleName The map style name to use.
-* @return An initialized map view, or `nil` if the map view was unable to be initialized. */
-- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken bundledStyleNamed:(NSString *)styleName;
+- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken;
-/** Initialize a map view with a given frame, style URL, and access token.
-* @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. */
+/** 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.
-* @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. */
-- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken;
-
- (instancetype)initWithFrame:(CGRect)frame __attribute__((unavailable("Instantiating an MGLMapView requires setting a style and/or an access token.")));
#pragma mark - Authorizing Access
/** @name Authorizing Access */
-/** Sets a Mapbox API access token for the map view.
-* @param accessToken A Mapbox API token. */
-- (void)setAccessToken:(NSString *)accessToken;
+/** Mapbox API access token for the map view. */
+@property (nonatomic) NSString *accessToken;
#pragma mark - Managing Constraints
@@ -63,7 +49,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
@@ -95,7 +81,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
@@ -148,7 +134,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
@@ -178,27 +164,22 @@
/** @name Styling the Map */
-/** Sets the map style.
-* @param styleJSON The map stylesheet as JSON text. */
-- (void)setStyleJSON:(NSString *)styleJSON;
-
-/** Returns the raw JSON style as a native dictionary object. */
-- (NSDictionary *)getRawStyle;
-
-/** Sets the raw JSON style as a native dictionary object with a transition animation.
-* @param style The style JSON as a dictionary object. */
-- (void)setRawStyle:(NSDictionary *)style;
-
-/** Returns the names of the styles bundled with the library. */
-- (NSArray *)bundledStyleNames;
+/** Mapbox map ID of the style currently displayed in the receiver, or `nil` if the style does not have a map ID.
+*
+* The style may lack a map ID if it is located at an HTTP, HTTPS, or local file URL. Use `styleURL` to get the URL in these cases.
+*
+* To display the default style, set this property to `nil`. */
+@property (nonatomic) NSString *mapID;
-/** Sets the map style to a named, bundled style.
-* @param styleName The map style name to use. */
-- (void)useBundledStyleNamed:(NSString *)styleName;
+/** Returns the URLs to the styles bundled with the library. */
+- (NSArray *)bundledStyleURLs;
-/** Sets the map style URL to use.
-* @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>`). */
-- (void)setStyleURL:(NSURL *)styleURL;
+/** URL of the style currently displayed in the receiver.
+*
+* The URL may be a full HTTP or HTTPS URL or a Mapbox URL indicating the style’s map ID (`mapbox://<user.style>`).
+*
+* To display the default style, set this property to `nil`. */
+@property (nonatomic) NSURL *styleURL;
#pragma mark - Annotating the Map
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index cdcfa27e42..764ff24ed8 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -96,6 +96,7 @@ public:
Duration getDefaultTransitionDuration();
void setStyleURL(const std::string& url);
void setStyleJSON(const std::string& json, const std::string& base = "");
+ std::string getStyleURL() const;
std::string getStyleJSON() const;
// Transition
diff --git a/ios/app/MBXViewController.mm b/ios/app/MBXViewController.mm
index c9973bcb7d..28ac68589a 100644
--- a/ios/app/MBXViewController.mm
+++ b/ios/app/MBXViewController.mm
@@ -16,10 +16,9 @@ static NSArray *const kStyleNames = @[
@"Basic",
@"Outdoors",
@"Satellite",
- @"Hybrid",
];
-static NSString *const kStyleVersion = @"v7";
+static NSString *const kStyleVersion = @"7";
@interface MBXViewController () <UIActionSheetDelegate, MGLMapViewDelegate>
@@ -246,10 +245,7 @@ mbgl::Settings_NSUserDefaults *settings = nullptr;
styleName = [kStyleNames objectAtIndex:index];
}
- [self.mapView useBundledStyleNamed:
- [[[styleName lowercaseString]
- stringByAppendingString:@"-"]
- stringByAppendingString:kStyleVersion]];
+ self.mapView.styleURL = [NSURL URLWithString:[NSString stringWithFormat:@"asset://styles/%@-v%@.json", styleName.lowercaseString, kStyleVersion]];
[titleButton setTitle:styleName forState:UIControlStateNormal];
}
diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm
index d486949175..741f659ad4 100644
--- a/platform/ios/MGLMapView.mm
+++ b/platform/ios/MGLMapView.mm
@@ -1,4 +1,5 @@
#import "MGLMapView.h"
+#import "MGLMapView+IBAdditions.h"
#import <mbgl/platform/log.hpp>
#import <mbgl/platform/gl.hpp>
@@ -15,6 +16,8 @@
#include <mbgl/util/geo.hpp>
#import "MGLTypes.h"
+#import "NSString+MGLAdditions.h"
+#import "NSProcessInfo+MGLAdditions.h"
#import "MGLAnnotation.h"
#import "MGLUserLocationAnnotationView.h"
#import "MGLUserLocation_Private.h"
@@ -43,8 +46,9 @@ const std::string &defaultCacheDatabase() {
static dispatch_once_t loadGLExtensions;
NSString *const MGLDefaultStyleName = @"Emerald";
-NSString *const MGLStyleVersion = @"v7";
+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};
@@ -52,6 +56,11 @@ const CGFloat MGLMinimumZoom = 3;
NSString *const MGLAnnotationIDKey = @"MGLAnnotationIDKey";
+static NSURL *MGLURLForBundledStyleNamed(NSString *styleName)
+{
+ return [NSURL URLWithString:[NSString stringWithFormat:@"asset://styles/%@.json", styleName]];
+}
+
#pragma mark - Private -
@interface MGLMapView () <UIGestureRecognizerDelegate, GLKViewDelegate, CLLocationManagerDelegate>
@@ -66,7 +75,7 @@ NSString *const MGLAnnotationIDKey = @"MGLAnnotationIDKey";
@property (nonatomic) UIPinchGestureRecognizer *pinch;
@property (nonatomic) UIRotationGestureRecognizer *rotate;
@property (nonatomic) UILongPressGestureRecognizer *quickZoom;
-@property (nonatomic) NSMutableArray *bundledStyleNames;
+@property (nonatomic) NSMutableArray *bundledStyleURLs;
@property (nonatomic) NSMapTable *annotationIDsByAnnotation;
@property (nonatomic) std::vector<uint32_t> annotationsNearbyLastTap;
@property (nonatomic, weak) id <MGLAnnotation> selectedAnnotation;
@@ -83,6 +92,11 @@ NSString *const MGLAnnotationIDKey = @"MGLAnnotationIDKey";
@end
@implementation MGLMapView
+{
+ BOOL _isTargetingInterfaceBuilder;
+ CLLocationDegrees _pendingLatitude;
+ CLLocationDegrees _pendingLongitude;
+}
#pragma mark - Setup & Teardown -
@@ -100,37 +114,22 @@ MBGLView *mbglView = nullptr;
mbgl::SQLiteCache *mbglFileCache = nullptr;
mbgl::DefaultFileSource *mbglFileSource = nullptr;
-- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken styleJSON:(NSString *)styleJSON
+- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self && [self commonInit])
{
- if (accessToken) [self setAccessToken:accessToken];
-
- if (styleJSON || 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 setStyleJSON:styleJSON];
- }
+ self.styleURL = nil;
+ return self;
}
- return self;
+ return nil;
}
-- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken bundledStyleNamed:(NSString *)styleName
+- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken
{
- self = [super initWithFrame:frame];
-
- if (self && [self commonInit])
- {
- if (accessToken) [self setAccessToken:accessToken];
- if (styleName) [self useBundledStyleNamed:styleName];
- }
-
- return self;
+ return [self initWithFrame:frame accessToken:accessToken styleURL:nil];
}
- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken styleURL:(NSURL *)styleURL
@@ -139,69 +138,72 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
if (self && [self commonInit])
{
- if (accessToken) [self setAccessToken:accessToken];
- if (styleURL) [self setStyleURL:styleURL];
+ self.accessToken = accessToken;
+ self.styleURL = styleURL;
}
return self;
}
-- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken
-{
- return [self initWithFrame:frame accessToken:accessToken styleJSON:nil];
-}
-
- (instancetype)initWithCoder:(NSCoder *)decoder
{
self = [super initWithCoder:decoder];
if (self && [self commonInit])
{
+ self.styleURL = nil;
return self;
}
return nil;
}
+- (NSString *)accessToken
+{
+ return @(mbglMap->getAccessToken().c_str()).mgl_stringOrNilIfEmpty;
+}
+
- (void)setAccessToken:(NSString *)accessToken
{
- if (accessToken)
- {
- mbglMap->setAccessToken((std::string)[accessToken UTF8String]);
- [MGLMapboxEvents setToken:accessToken];
- }
+ mbglMap->setAccessToken((std::string)[accessToken UTF8String]);
+ [MGLMapboxEvents setToken:accessToken.mgl_stringOrNilIfEmpty];
}
-- (void)setStyleJSON:(NSString *)styleJSON
++ (NSSet *)keyPathsForValuesAffectingStyleURL
{
- if ( ! styleJSON)
- {
- [self useBundledStyleNamed:[[[MGLDefaultStyleName lowercaseString]
- stringByAppendingString:@"-"]
- stringByAppendingString:MGLStyleVersion]];
- }
- else
- {
- mbglMap->setStyleJSON((std::string)[styleJSON UTF8String]);
- }
+ return [NSSet setWithObjects:@"mapID", @"accessToken", nil];
}
-- (void)setStyleURL:(NSURL *)styleURL
+- (NSURL *)styleURL
{
- std::string styleURLString([[styleURL absoluteString] UTF8String]);
+ NSString *styleURLString = @(mbglMap->getStyleURL().c_str()).mgl_stringOrNilIfEmpty;
+ return styleURLString ? [NSURL URLWithString:styleURLString] : nil;
+}
- if ( ! [styleURL scheme])
+- (void)setStyleURL:(NSURL *)styleURL
+{
+ if (_isTargetingInterfaceBuilder) return;
+
+ if ( ! styleURL)
{
- mbglMap->setStyleURL(std::string("asset://") + styleURLString);
+ styleURL = MGLURLForBundledStyleNamed([NSString stringWithFormat:@"%@-v%@",
+ MGLDefaultStyleName.lowercaseString,
+ MGLStyleVersion]);
}
- else
+
+ if ( ! [styleURL scheme])
{
- mbglMap->setStyleURL(styleURLString);
+ // Assume a relative path into the developer’s bundle.
+ styleURL = [[NSBundle mainBundle] URLForResource:styleURL.path withExtension:nil];
}
+
+ mbglMap->setStyleURL([[styleURL absoluteString] UTF8String]);
}
- (BOOL)commonInit
{
+ _isTargetingInterfaceBuilder = NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent;
+
// create context
//
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
@@ -242,7 +244,8 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
// load extensions
//
- dispatch_once(&loadGLExtensions, ^{
+ dispatch_once(&loadGLExtensions, ^
+ {
const std::string extensions = (char *)glGetString(GL_EXTENSIONS);
using namespace mbgl;
@@ -365,14 +368,19 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
// set initial position
//
mbglMap->setLatLngZoom(mbgl::LatLng(0, 0), mbglMap->getMinZoom());
+ _pendingLatitude = NAN;
+ _pendingLongitude = NAN;
// setup change delegate queue
//
_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();
@@ -559,7 +567,11 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)layoutSubviews
{
[super layoutSubviews];
- mbglMap->triggerUpdate();
+
+ if ( ! _isTargetingInterfaceBuilder)
+ {
+ mbglMap->triggerUpdate();
+ }
}
#pragma mark - Life Cycle -
@@ -1052,6 +1064,21 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
#pragma mark - Properties -
++ (NSSet *)keyPathsForValuesAffectingZoomEnabled
+{
+ return [NSSet setWithObject:@"allowsZooming"];
+}
+
++ (NSSet *)keyPathsForValuesAffectingScrollEnabled
+{
+ return [NSSet setWithObject:@"allowsScrolling"];
+}
+
++ (NSSet *)keyPathsForValuesAffectingRotateEnabled
+{
+ return [NSSet setWithObject:@"allowsRotating"];
+}
+
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
@@ -1119,6 +1146,11 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
#pragma mark - Geography -
++ (NSSet *)keyPathsForValuesAffectingCenterCoordinate
+{
+ return [NSSet setWithObjects:@"latitude", @"longitude", nil];
+}
+
- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated preservingTracking:(BOOL)tracking
{
self.userTrackingMode = (tracking ? self.userTrackingMode : MGLUserTrackingModeNone);
@@ -1284,54 +1316,44 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
#pragma mark - Styling -
-- (NSDictionary *)getRawStyle
+- (NSArray *)bundledStyleURLs
{
- const std::string styleJSON = mbglMap->getStyleJSON();
+ if ( ! _bundledStyleURLs)
+ {
+ NSString *stylesPath = [[MGLMapView resourceBundlePath] stringByAppendingPathComponent:@"styles"];
- return [NSJSONSerialization JSONObjectWithData:[@(styleJSON.c_str()) dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
-}
+ _bundledStyleURLs = [NSMutableArray array];
-- (void)setRawStyle:(NSDictionary *)style
-{
- NSData *data = [NSJSONSerialization dataWithJSONObject:style options:0 error:nil];
+ NSArray *bundledStyleNamesWithExtensions = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:stylesPath error:nil];
+ for (NSString *fileName in bundledStyleNamesWithExtensions)
+ {
+ [_bundledStyleURLs addObject:MGLURLForBundledStyleNamed([fileName stringByDeletingPathExtension])];
+ }
+ }
- [self setStyleJSON:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]];
+ return [NSArray arrayWithArray:_bundledStyleURLs];
}
-- (NSArray *)bundledStyleNames
++ (NSSet *)keyPathsForValuesAffectingMapID
{
- if (!_bundledStyleNames) {
- NSString *stylesPath = [[MGLMapView resourceBundlePath] stringByAppendingString:@"/styles"];
-
- _bundledStyleNames = [NSMutableArray array];
-
- NSArray *bundledStyleNamesWithExtensions = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:stylesPath error:nil];
- NSString *hybridStylePrefix = @"hybrid-";
- NSString *satelliteStylePrefix = @"satellite-";
- for (NSString *fileName in bundledStyleNamesWithExtensions) {
- NSString *styleName = [fileName stringByDeletingPathExtension];
- [_bundledStyleNames addObject:styleName];
-
- // Add satellite raster & "hybrid" (satellite raster + vector contours & labels)
- if ([styleName hasPrefix:satelliteStylePrefix]) {
- [_bundledStyleNames addObject:[hybridStylePrefix stringByAppendingString:[styleName substringFromIndex:[satelliteStylePrefix length]]]];
- }
- }
- }
+ return [NSSet setWithObjects:@"styleURL", @"accessToken", nil];
+}
- return [NSArray arrayWithArray:_bundledStyleNames];
+- (NSString *)mapID
+{
+ NSURL *styleURL = self.styleURL;
+ return [styleURL.scheme isEqualToString:@"mapbox"] ? styleURL.host.mgl_stringOrNilIfEmpty : nil;
}
-- (void)useBundledStyleNamed:(NSString *)styleName
+- (void)setMapID:(NSString *)mapID
{
- NSString *hybridStylePrefix = @"hybrid-";
- BOOL isHybrid = [styleName hasPrefix:hybridStylePrefix];
- if (isHybrid) {
- styleName = [@"satellite-" stringByAppendingString:[styleName substringFromIndex:[hybridStylePrefix length]]];
+ if (mapID)
+ {
+ self.styleURL = [NSURL URLWithString:[NSString stringWithFormat:@"mapbox://%@", mapID]];
}
- [self setStyleURL:[NSURL URLWithString:[NSString stringWithFormat:@"styles/%@.json", styleName]]];
- if (isHybrid) {
- [self setStyleClasses:@[@"contours", @"labels"]];
+ else
+ {
+ self.styleURL = nil;
}
}
@@ -2215,6 +2237,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:
@@ -2272,3 +2423,104 @@ class MBGLView : public mbgl::View
};
@end
+
+@implementation MGLMapView (IBAdditions)
+
++ (NSSet *)keyPathsForValuesAffectingLatitude
+{
+ return [NSSet setWithObject:@"centerCoordinate"];
+}
+
+- (double)latitude
+{
+ return self.centerCoordinate.latitude;
+}
+
+- (void)setLatitude:(double)latitude
+{
+ if ( ! isnan(_pendingLongitude))
+ {
+ self.centerCoordinate = CLLocationCoordinate2DMake(latitude, _pendingLongitude);
+ _pendingLatitude = NAN;
+ _pendingLongitude = NAN;
+ }
+ else
+ {
+ // Not enough info to make a valid center coordinate yet. Stash this
+ // latitude away until the longitude is set too.
+ _pendingLatitude = latitude;
+ }
+}
+
++ (NSSet *)keyPathsForValuesAffectingLongitude
+{
+ return [NSSet setWithObject:@"centerCoordinate"];
+}
+
+- (double)longitude
+{
+ return self.centerCoordinate.longitude;
+}
+
+- (void)setLongitude:(double)longitude
+{
+ if ( ! isnan(_pendingLatitude))
+ {
+ self.centerCoordinate = CLLocationCoordinate2DMake(_pendingLatitude, longitude);
+ _pendingLatitude = NAN;
+ _pendingLongitude = NAN;
+ }
+ else
+ {
+ // Not enough info to make a valid center coordinate yet. Stash this
+ // longitude away until the latitude is set too.
+ _pendingLongitude = longitude;
+ }
+}
+
++ (NSSet *)keyPathsForValuesAffectingAllowsZooming
+{
+ return [NSSet setWithObject:@"zoomEnabled"];
+}
+
+- (BOOL)allowsZooming
+{
+ return self.zoomEnabled;
+}
+
+- (void)setAllowsZooming:(BOOL)allowsZooming
+{
+ self.zoomEnabled = allowsZooming;
+}
+
++ (NSSet *)keyPathsForValuesAffectingAllowsScrolling
+{
+ return [NSSet setWithObject:@"scrollEnabled"];
+}
+
+- (BOOL)allowsScrolling
+{
+ return self.scrollEnabled;
+}
+
+- (void)setAllowsScrolling:(BOOL)allowsScrolling
+{
+ self.scrollEnabled = allowsScrolling;
+}
+
++ (NSSet *)keyPathsForValuesAffectingAllowsRotating
+{
+ return [NSSet setWithObject:@"rotateEnabled"];
+}
+
+- (BOOL)allowsRotating
+{
+ return self.rotateEnabled;
+}
+
+- (void)setAllowsRotating:(BOOL)allowsRotating
+{
+ self.rotateEnabled = allowsRotating;
+}
+
+@end
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
diff --git a/platform/ios/NSString+MGLAdditions.h b/platform/ios/NSString+MGLAdditions.h
new file mode 100644
index 0000000000..913f1cacda
--- /dev/null
+++ b/platform/ios/NSString+MGLAdditions.h
@@ -0,0 +1,8 @@
+#import <Foundation/Foundation.h>
+
+@interface NSString (MGLAdditions)
+
+/** Returns the receiver if non-empty or nil if empty. */
+- (NSString *)mgl_stringOrNilIfEmpty;
+
+@end
diff --git a/platform/ios/NSString+MGLAdditions.m b/platform/ios/NSString+MGLAdditions.m
new file mode 100644
index 0000000000..d80c34cef0
--- /dev/null
+++ b/platform/ios/NSString+MGLAdditions.m
@@ -0,0 +1,10 @@
+#import "NSString+MGLAdditions.h"
+
+@implementation NSString (MGLAdditions)
+
+- (NSString *)mgl_stringOrNilIfEmpty
+{
+ return self.length ? self : nil;
+}
+
+@end
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index 09e6b08a1e..ac2be3f060 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -396,6 +396,10 @@ void Map::setup() {
painter->setup();
}
+std::string Map::getStyleURL() const {
+ return data->getStyleInfo().url;
+}
+
void Map::setStyleURL(const std::string &url) {
assert(Environment::currentlyOn(ThreadType::Main));
@@ -756,7 +760,7 @@ void Map::reloadStyle() {
Log::Error(Event::Setup, "loading style failed: %s", res.message.c_str());
}
});
- } else {
+ } else if (!styleInfo.json.empty()) {
// We got JSON data directly.
loadStyleJSON(styleInfo.json, styleInfo.base);
}