Merge pull request #1184 from mapbox/1070-kvo
Style API cleanup, designables, inspectables
This commit is contained in:
@@ -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',
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
33
include/mbgl/ios/MGLMapView+IBAdditions.h
Normal file
33
include/mbgl/ios/MGLMapView+IBAdditions.h
Normal file
@@ -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
|
||||
@@ -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.
|
||||
* @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;
|
||||
|
||||
/** 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. */
|
||||
- (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 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.
|
||||
* @return An initialized map view, or `nil` if the map view was unable to be initialized. */
|
||||
- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken;
|
||||
|
||||
/** 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;
|
||||
|
||||
- (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;
|
||||
/** 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;
|
||||
|
||||
/** Returns the raw JSON style as a native dictionary object. */
|
||||
- (NSDictionary *)getRawStyle;
|
||||
/** Returns the URLs to the styles bundled with the library. */
|
||||
- (NSArray *)bundledStyleURLs;
|
||||
|
||||
/** 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;
|
||||
|
||||
/** Sets the map style to a named, bundled style.
|
||||
* @param styleName The map style name to use. */
|
||||
- (void)useBundledStyleNamed:(NSString *)styleName;
|
||||
|
||||
/** 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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
- (void)setAccessToken:(NSString *)accessToken
|
||||
- (NSString *)accessToken
|
||||
{
|
||||
if (accessToken)
|
||||
{
|
||||
mbglMap->setAccessToken((std::string)[accessToken UTF8String]);
|
||||
[MGLMapboxEvents setToken:accessToken];
|
||||
}
|
||||
return @(mbglMap->getAccessToken().c_str()).mgl_stringOrNilIfEmpty;
|
||||
}
|
||||
|
||||
- (void)setStyleJSON:(NSString *)styleJSON
|
||||
- (void)setAccessToken:(NSString *)accessToken
|
||||
{
|
||||
if ( ! styleJSON)
|
||||
{
|
||||
[self useBundledStyleNamed:[[[MGLDefaultStyleName lowercaseString]
|
||||
stringByAppendingString:@"-"]
|
||||
stringByAppendingString:MGLStyleVersion]];
|
||||
}
|
||||
else
|
||||
{
|
||||
mbglMap->setStyleJSON((std::string)[styleJSON UTF8String]);
|
||||
}
|
||||
mbglMap->setAccessToken((std::string)[accessToken UTF8String]);
|
||||
[MGLMapboxEvents setToken:accessToken.mgl_stringOrNilIfEmpty];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingStyleURL
|
||||
{
|
||||
return [NSSet setWithObjects:@"mapID", @"accessToken", nil];
|
||||
}
|
||||
|
||||
- (NSURL *)styleURL
|
||||
{
|
||||
NSString *styleURLString = @(mbglMap->getStyleURL().c_str()).mgl_stringOrNilIfEmpty;
|
||||
return styleURLString ? [NSURL URLWithString:styleURLString] : nil;
|
||||
}
|
||||
|
||||
- (void)setStyleURL:(NSURL *)styleURL
|
||||
{
|
||||
std::string styleURLString([[styleURL absoluteString] UTF8String]);
|
||||
|
||||
if (_isTargetingInterfaceBuilder) return;
|
||||
|
||||
if ( ! styleURL)
|
||||
{
|
||||
styleURL = MGLURLForBundledStyleNamed([NSString stringWithFormat:@"%@-v%@",
|
||||
MGLDefaultStyleName.lowercaseString,
|
||||
MGLStyleVersion]);
|
||||
}
|
||||
|
||||
if ( ! [styleURL scheme])
|
||||
{
|
||||
mbglMap->setStyleURL(std::string("asset://") + styleURLString);
|
||||
}
|
||||
else
|
||||
{
|
||||
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];
|
||||
}
|
||||
|
||||
- (void)setRawStyle:(NSDictionary *)style
|
||||
{
|
||||
NSData *data = [NSJSONSerialization dataWithJSONObject:style options:0 error:nil];
|
||||
|
||||
[self setStyleJSON:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]];
|
||||
}
|
||||
|
||||
- (NSArray *)bundledStyleNames
|
||||
{
|
||||
if (!_bundledStyleNames) {
|
||||
NSString *stylesPath = [[MGLMapView resourceBundlePath] stringByAppendingString:@"/styles"];
|
||||
|
||||
_bundledStyleNames = [NSMutableArray array];
|
||||
_bundledStyleURLs = [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]]]];
|
||||
}
|
||||
for (NSString *fileName in bundledStyleNamesWithExtensions)
|
||||
{
|
||||
[_bundledStyleURLs addObject:MGLURLForBundledStyleNamed([fileName stringByDeletingPathExtension])];
|
||||
}
|
||||
}
|
||||
|
||||
return [NSArray arrayWithArray:_bundledStyleNames];
|
||||
return [NSArray arrayWithArray:_bundledStyleURLs];
|
||||
}
|
||||
|
||||
- (void)useBundledStyleNamed:(NSString *)styleName
|
||||
+ (NSSet *)keyPathsForValuesAffectingMapID
|
||||
{
|
||||
NSString *hybridStylePrefix = @"hybrid-";
|
||||
BOOL isHybrid = [styleName hasPrefix:hybridStylePrefix];
|
||||
if (isHybrid) {
|
||||
styleName = [@"satellite-" stringByAppendingString:[styleName substringFromIndex:[hybridStylePrefix length]]];
|
||||
return [NSSet setWithObjects:@"styleURL", @"accessToken", nil];
|
||||
}
|
||||
|
||||
- (NSString *)mapID
|
||||
{
|
||||
NSURL *styleURL = self.styleURL;
|
||||
return [styleURL.scheme isEqualToString:@"mapbox"] ? styleURL.host.mgl_stringOrNilIfEmpty : nil;
|
||||
}
|
||||
|
||||
- (void)setMapID:(NSString *)mapID
|
||||
{
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
|
||||
7
platform/ios/NSProcessInfo+MGLAdditions.h
Normal file
7
platform/ios/NSProcessInfo+MGLAdditions.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NSProcessInfo (MGLAdditions)
|
||||
|
||||
- (BOOL)mgl_isInterfaceBuilderDesignablesAgent;
|
||||
|
||||
@end
|
||||
10
platform/ios/NSProcessInfo+MGLAdditions.m
Normal file
10
platform/ios/NSProcessInfo+MGLAdditions.m
Normal file
@@ -0,0 +1,10 @@
|
||||
#import "NSProcessInfo+MGLAdditions.h"
|
||||
|
||||
@implementation NSProcessInfo (MGLAdditions)
|
||||
|
||||
- (BOOL)mgl_isInterfaceBuilderDesignablesAgent
|
||||
{
|
||||
return [self.processName isEqualToString:@"IBDesignablesAgentCocoaTouch"];
|
||||
}
|
||||
|
||||
@end
|
||||
8
platform/ios/NSString+MGLAdditions.h
Normal file
8
platform/ios/NSString+MGLAdditions.h
Normal file
@@ -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
|
||||
10
platform/ios/NSString+MGLAdditions.m
Normal file
10
platform/ios/NSString+MGLAdditions.m
Normal file
@@ -0,0 +1,10 @@
|
||||
#import "NSString+MGLAdditions.h"
|
||||
|
||||
@implementation NSString (MGLAdditions)
|
||||
|
||||
- (NSString *)mgl_stringOrNilIfEmpty
|
||||
{
|
||||
return self.length ? self : nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user