diff --git a/platform/darwin/src/MGLMapSnapshotter.h b/platform/darwin/src/MGLMapSnapshotter.h index 2a30a9d41..b6435c1fb 100644 --- a/platform/darwin/src/MGLMapSnapshotter.h +++ b/platform/darwin/src/MGLMapSnapshotter.h @@ -2,9 +2,12 @@ #import "MGLTypes.h" #import "MGLGeometry.h" #import "MGLMapCamera.h" +#import "MGLStyle.h" NS_ASSUME_NONNULL_BEGIN +@protocol MGLMapSnapshotterDelegate; + /** An overlay that is placed within a `MGLMapSnapshot`. To access this object, use `-[MGLMapSnapshotter startWithOverlayHandler:completionHandler:]`. @@ -177,8 +180,9 @@ typedef void (^MGLMapSnapshotCompletionHandler)(MGLMapSnapshot* _Nullable snapsh An `MGLMapSnapshotter` generates static raster images of the map. Each snapshot image depicts a portion of a map defined by an `MGLMapSnapshotOptions` object you provide. The snapshotter generates an `MGLMapSnapshot` object - asynchronously, passing it into a completion handler once tiles and other - resources needed for the snapshot are finished loading. + asynchronously, calling `MGLMapSnapshotterDelegate` methods if defined, then + passing it into a completion handler once tiles and other resources needed for + the snapshot are finished loading. You can change the snapshotter’s options at any time and reuse the snapshotter for multiple distinct snapshots; however, the snapshotter can only generate one @@ -220,7 +224,7 @@ typedef void (^MGLMapSnapshotCompletionHandler)(MGLMapSnapshot* _Nullable snapsh object's style, camera, and view bounds. */ MGL_EXPORT -@interface MGLMapSnapshotter : NSObject +@interface MGLMapSnapshotter : NSObject - (instancetype)init NS_UNAVAILABLE; @@ -284,6 +288,78 @@ MGL_EXPORT */ @property (nonatomic, readonly, getter=isLoading) BOOL loading; +/** + The snapshotter’s delegate. + + The delegate is responsible for responding to significant changes during the + snapshotting process, such as the style loading. Implement a delegate to + customize the style that is depicted by the snapshot. + + You set the delegate after initializing the snapshotter but before receiving + the snapshot, typically before starting the snapshot. The snapshotter keeps a + weak reference to its delegate, so you must keep a strong reference to it to + ensure that your style customizations apply. + */ +@property (nonatomic, weak) id delegate; + +/** + The style displayed in the resulting snapshot. + + Unlike the `MGLMapSnapshotOptions.styleURL` property, this property is set to + an object that allows you to manipulate every aspect of the style locally. + + This property is set to `nil` until the style finishes loading. If the style + has failed to load, this property is set to `nil`. Because the style loads + asynchronously, you should manipulate it in the + `-[MGLMapSnapshotterDelegate mapSnapshotter:didFinishLoadingStyle:]` method. It + is not possible to manipulate the style before it has finished loading. + + @note The default styles provided by Mapbox contain sources and layers with + identifiers that will change over time. Applications that use APIs that + manipulate a style’s sources and layers must first set the style URL to an + explicitly versioned style using a convenience method like + `+[MGLStyle outdoorsStyleURLWithVersion:]` or a manually constructed + `NSURL`. + */ +@property (nonatomic, readonly, nullable) MGLStyle *style; + +@end + +/** + Optional methods about significant events when creating a snapshot using an + `MGLMapSnapshotter` object. + */ +@protocol MGLMapSnapshotterDelegate +@optional + +/** + Tells the delegate that the snapshotter was unable to load data needed for + snapshotting the map. + + This method may be called for a variety of reasons, including a network + connection failure or a failure to fetch the style from the server. You can use + the given error message to notify the user that map data is unavailable. + + @param snapshotter The snapshotter that is unable to load the data. + @param error The reason the data could not be loaded. +*/ +- (void)mapSnapshotterDidFail:(MGLMapSnapshotter *)snapshotter withError:(NSError *)error; + +/** + Tells the delegate that the snapshotter has just finished loading a style. + + This method is called in response to + `-[MGLMapSnapshotter startWithQueue:completionHandler:]` as long as the + `MGLMapSnapshotter.delegate` property is set. Changes to sources or layers of + the style being snapshotted do not cause this method to be called. + + @param snapshotter The snapshotter that has just loaded a style. + @param style The style that was loaded. + */ +- (void)mapSnapshotter:(MGLMapSnapshotter *)snapshotter didFinishLoadingStyle:(MGLStyle *)style; + +- (void)mapSnapshotter:(MGLMapSnapshotter *)snapshotter didFailLoadingImageNamed:(NSString *)name; + @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLMapSnapshotter.mm b/platform/darwin/src/MGLMapSnapshotter.mm index c3cf223cf..1e7547c0b 100644 --- a/platform/darwin/src/MGLMapSnapshotter.mm +++ b/platform/darwin/src/MGLMapSnapshotter.mm @@ -11,8 +11,7 @@ #import "MGLOfflineStorage_Private.h" #import "MGLGeometry_Private.h" -#import "NSBundle+MGLAdditions.h" -#import "MGLStyle.h" +#import "MGLStyle_Private.h" #import "MGLAttributionInfo_Private.h" #import "MGLLoggingConfiguration_Private.h" #import "MGLRendererConfiguration.h" @@ -30,6 +29,8 @@ #import #endif +#import "NSBundle+MGLAdditions.h" + const CGPoint MGLLogoImagePosition = CGPointMake(8, 8); const CGFloat MGLSnapshotterMinimumPixelSize = 64; @@ -37,6 +38,41 @@ MGLImage *MGLAttributedSnapshot(mbgl::MapSnapshotter::Attributions attributions, MGLMapSnapshot *MGLSnapshotWithDecoratedImage(MGLImage *mglImage, MGLMapSnapshotOptions *options, mbgl::MapSnapshotter::Attributions attributions, mbgl::MapSnapshotter::PointForFn pointForFn, mbgl::MapSnapshotter::LatLngForFn latLngForFn, MGLMapSnapshotOverlayHandler overlayHandler, NSError * _Nullable *outError); NSArray *MGLAttributionInfosFromAttributions(mbgl::MapSnapshotter::Attributions attributions); +class MGLMapSnapshotterDelegateHost: public mbgl::MapSnapshotterObserver { +public: + MGLMapSnapshotterDelegateHost(MGLMapSnapshotter *snapshotter_) : snapshotter(snapshotter_) {} + + void onDidFailLoadingStyle(const std::string& errorMessage) { + MGLMapSnapshotter *strongSnapshotter = snapshotter; + if ([strongSnapshotter.delegate respondsToSelector:@selector(mapSnapshotterDidFail:withError:)]) { + NSString *description = @(errorMessage.c_str()); + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedStringWithDefaultValue(@"SNAPSHOT_LOAD_STYLE_FAILED_DESC", nil, nil, @"The snapshot failed because the style can’t be loaded.", @"User-friendly error description"), + NSLocalizedFailureReasonErrorKey: description, + }; + NSError *error = [NSError errorWithDomain:MGLErrorDomain code:MGLErrorCodeLoadStyleFailed userInfo:userInfo]; + [strongSnapshotter.delegate mapSnapshotterDidFail:snapshotter withError:error]; + } + } + + void onDidFinishLoadingStyle() { + MGLMapSnapshotter *strongSnapshotter = snapshotter; + if ([strongSnapshotter.delegate respondsToSelector:@selector(mapSnapshotter:didFinishLoadingStyle:)]) { + [strongSnapshotter.delegate mapSnapshotter:snapshotter didFinishLoadingStyle:snapshotter.style]; + } + } + + void onStyleImageMissing(const std::string& imageName) { + MGLMapSnapshotter *strongSnapshotter = snapshotter; + if ([strongSnapshotter.delegate respondsToSelector:@selector(mapSnapshotter:didFailLoadingImageNamed:)]) { + [strongSnapshotter.delegate mapSnapshotter:snapshotter didFailLoadingImageNamed:@(imageName.c_str())]; + } + } + +private: + __weak MGLMapSnapshotter *snapshotter; +}; + @interface MGLMapSnapshotOverlay() @property (nonatomic, assign) CGFloat scale; - (instancetype)initWithContext:(CGContextRef)context scale:(CGFloat)scale pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn; @@ -208,6 +244,7 @@ NSArray *MGLAttributionInfosFromAttributions(mbgl::MapSnap @implementation MGLMapSnapshotter { std::unique_ptr _mbglMapSnapshotter; + std::unique_ptr _delegateHost; } - (void)dealloc { @@ -660,6 +697,7 @@ NSArray *MGLAttributionInfosFromAttributions(mbgl::MapSnap _mbglMapSnapshotter->cancel(); } _mbglMapSnapshotter.reset(); + _delegateHost.reset(); } - (void)configureWithOptions:(MGLMapSnapshotOptions *)options { @@ -682,8 +720,9 @@ NSArray *MGLAttributionInfosFromAttributions(mbgl::MapSnap .withAssetPath(NSBundle.mainBundle.resourceURL.path.UTF8String); // Create the snapshotter + _delegateHost = std::make_unique(self); _mbglMapSnapshotter = std::make_unique( - size, pixelRatio, resourceOptions, mbgl::MapSnapshotterObserver::nullObserver(), config.localFontFamilyName); + size, pixelRatio, resourceOptions, *_delegateHost, config.localFontFamilyName); _mbglMapSnapshotter->setStyleURL(std::string(options.styleURL.absoluteString.UTF8String)); @@ -703,4 +742,11 @@ NSArray *MGLAttributionInfosFromAttributions(mbgl::MapSnap } } +- (MGLStyle *)style { + if (!_mbglMapSnapshotter) { + return nil; + } + return [[MGLStyle alloc] initWithRawStyle:&_mbglMapSnapshotter->getStyle() stylable:self]; +} + @end diff --git a/platform/darwin/src/MGLOpenGLStyleLayer.mm b/platform/darwin/src/MGLOpenGLStyleLayer.mm index 8c9e4b02a..8502a3ff6 100644 --- a/platform/darwin/src/MGLOpenGLStyleLayer.mm +++ b/platform/darwin/src/MGLOpenGLStyleLayer.mm @@ -9,48 +9,7 @@ #include #include -class MGLOpenGLLayerHost : public mbgl::style::CustomLayerHost { -public: - MGLOpenGLLayerHost(MGLOpenGLStyleLayer *styleLayer) { - layerRef = styleLayer; - layer = nil; - } - - void initialize() { - if (layerRef == nil) return; - else if (layer == nil) layer = layerRef; - - [layer didMoveToMapView:layer.style.mapView]; - } - - void render(const mbgl::style::CustomLayerRenderParameters ¶ms) { - if(!layer) return; - - MGLStyleLayerDrawingContext drawingContext = { - .size = CGSizeMake(params.width, params.height), - .centerCoordinate = CLLocationCoordinate2DMake(params.latitude, params.longitude), - .zoomLevel = params.zoom, - .direction = mbgl::util::wrap(params.bearing, 0., 360.), - .pitch = static_cast(params.pitch), - .fieldOfView = static_cast(params.fieldOfView), - .projectionMatrix = MGLMatrix4Make(params.projectionMatrix) - }; - [layer drawInMapView:layer.style.mapView withContext:drawingContext]; - } - - void contextLost() {} - - void deinitialize() { - if (layer == nil) return; - - [layer willMoveFromMapView:layer.style.mapView]; - layerRef = layer; - layer = nil; - } -private: - __weak MGLOpenGLStyleLayer * layerRef; - MGLOpenGLStyleLayer * layer = nil; -}; +class MGLOpenGLLayerHost; /** An `MGLOpenGLStyleLayer` is a style layer that is rendered by OpenGL code that @@ -74,6 +33,8 @@ private: @property (nonatomic, readonly) mbgl::style::CustomLayer *rawLayer; +@property (nonatomic, readonly, nullable) MGLMapView *mapView; + /** The style currently containing the layer. @@ -107,13 +68,20 @@ private: return (mbgl::style::CustomLayer *)super.rawLayer; } +- (MGLMapView *)mapView { + if ([self.style.stylable isKindOfClass:[MGLMapView class]]) { + return (MGLMapView *)self.style.stylable; + } + return nil; +} + #if TARGET_OS_IPHONE - (EAGLContext *)context { - return self.style.mapView.context; + return self.mapView.context; } #else - (CGLContextObj)context { - return self.style.mapView.context; + return self.mapView.context; } #endif @@ -191,11 +159,60 @@ private: causing the `-drawInMapView:withContext:` method to be called. */ - (void)setNeedsDisplay { - [self.style.mapView setNeedsRerender]; + [self.mapView setNeedsRerender]; } @end +class MGLOpenGLLayerHost : public mbgl::style::CustomLayerHost { +public: + MGLOpenGLLayerHost(MGLOpenGLStyleLayer *styleLayer) { + layerRef = styleLayer; + layer = nil; + } + + void initialize() { + if (layerRef == nil) return; + else if (layer == nil) layer = layerRef; + + if (layer.mapView) { + [layer didMoveToMapView:layer.mapView]; + } + } + + void render(const mbgl::style::CustomLayerRenderParameters ¶ms) { + if(!layer) return; + + MGLStyleLayerDrawingContext drawingContext = { + .size = CGSizeMake(params.width, params.height), + .centerCoordinate = CLLocationCoordinate2DMake(params.latitude, params.longitude), + .zoomLevel = params.zoom, + .direction = mbgl::util::wrap(params.bearing, 0., 360.), + .pitch = static_cast(params.pitch), + .fieldOfView = static_cast(params.fieldOfView), + .projectionMatrix = MGLMatrix4Make(params.projectionMatrix) + }; + if (layer.mapView) { + [layer drawInMapView:layer.mapView withContext:drawingContext]; + } + } + + void contextLost() {} + + void deinitialize() { + if (layer == nil) return; + + if (layer.mapView) { + [layer willMoveFromMapView:layer.mapView]; + } + layerRef = layer; + layer = nil; + } +private: + __weak MGLOpenGLStyleLayer * layerRef; + MGLOpenGLStyleLayer * layer = nil; +}; + namespace mbgl { MGLStyleLayer* OpenGLStyleLayerPeerFactory::createPeer(style::Layer* rawLayer) { diff --git a/platform/darwin/src/MGLShapeSource.mm b/platform/darwin/src/MGLShapeSource.mm index 2590865ac..864b4c546 100644 --- a/platform/darwin/src/MGLShapeSource.mm +++ b/platform/darwin/src/MGLShapeSource.mm @@ -243,8 +243,9 @@ mbgl::Immutable MGLGeoJSONOptionsFromDictionary(NSD } std::vector features; - if (self.mapView) { - features = self.mapView.renderer->querySourceFeatures(self.rawSource->getID(), { {}, optionalFilter }); + if ([self.stylable isKindOfClass:[MGLMapView class]]) { + MGLMapView *mapView = (MGLMapView *)self.stylable; + features = mapView.renderer->querySourceFeatures(self.rawSource->getID(), { {}, optionalFilter }); } return MGLFeaturesFromMBGLFeatures(features); } @@ -256,7 +257,7 @@ mbgl::Immutable MGLGeoJSONOptionsFromDictionary(NSD mbgl::optional extensionValue; // Check parameters - if (!self.rawSource || !self.mapView || !cluster) { + if (!self.rawSource || !self.stylable || !cluster) { return extensionValue; } @@ -269,11 +270,14 @@ mbgl::Immutable MGLGeoJSONOptionsFromDictionary(NSD auto clusterFeature = geoJSON.get(); - extensionValue = self.mapView.renderer->queryFeatureExtensions(self.rawSource->getID(), - clusterFeature, - "supercluster", - extension, - options); + if ([self.stylable isKindOfClass:[MGLMapView class]]) { + MGLMapView *mapView = (MGLMapView *)self.stylable; + extensionValue = mapView.renderer->queryFeatureExtensions(self.rawSource->getID(), + clusterFeature, + "supercluster", + extension, + options); + } return extensionValue; } diff --git a/platform/darwin/src/MGLSource.mm b/platform/darwin/src/MGLSource.mm index 50236904b..51f1fd9cc 100644 --- a/platform/darwin/src/MGLSource.mm +++ b/platform/darwin/src/MGLSource.mm @@ -19,7 +19,7 @@ const MGLExceptionName MGLInvalidStyleSourceException = @"MGLInvalidStyleSourceE // special internal source types like mbgl::AnnotationSource. @property (nonatomic, readonly) mbgl::style::Source *rawSource; -@property (nonatomic, readonly, weak) MGLMapView *mapView; +@property (nonatomic, readonly, weak) id stylable; @end @@ -37,12 +37,12 @@ const MGLExceptionName MGLInvalidStyleSourceException = @"MGLInvalidStyleSourceE return self; } -- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource mapView:(MGLMapView *)mapView { +- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource stylable:(id )stylable { NSString *identifier = @(rawSource->getID().c_str()); if (self = [self initWithIdentifier:identifier]) { _weakSource = rawSource->makeWeakPtr(); rawSource->peer = SourceWrapper { self }; - _mapView = mapView; + _stylable = stylable; } return self; } @@ -53,24 +53,24 @@ const MGLExceptionName MGLInvalidStyleSourceException = @"MGLInvalidStyleSourceE } - (instancetype)initWithPendingSource:(std::unique_ptr)pendingSource { - if (self = [self initWithRawSource:pendingSource.get() mapView:nil]) { + if (self = [self initWithRawSource:pendingSource.get() stylable:nil]) { _pendingSource = std::move(pendingSource); } return self; } -- (void)addToMapView:(MGLMapView *)mapView { +- (void)addToStylable:(id )stylable { if (_pendingSource == nullptr) { [NSException raise:MGLRedundantSourceException format:@"This instance %@ was already added to %@. Adding the same source instance " \ - "to the style more than once is invalid.", self, mapView.style]; + "to the style more than once is invalid.", self, stylable.style]; } - _mapView = mapView; - _mapView.style.rawStyle->addSource(std::move(_pendingSource)); + _stylable = stylable; + _stylable.style.rawStyle->addSource(std::move(_pendingSource)); } -- (BOOL)removeFromMapView:(MGLMapView *)mapView error:(NSError * __nullable * __nullable)outError { +- (BOOL)removeFromStylable:(id )mapView error:(NSError * __nullable * __nullable)outError { MGLAssertStyleSourceIsValid(); BOOL removed = NO; @@ -81,7 +81,7 @@ const MGLExceptionName MGLInvalidStyleSourceException = @"MGLInvalidStyleSourceE if (removedSource) { removed = YES; _pendingSource = std::move(removedSource); - _mapView = nil; + _stylable = nil; } else if (outError) { NSString *localizedDescription = [NSString stringWithFormat: NSLocalizedStringWithDefaultValue(@"REMOVE_SRC_FAIL_IN_USE_FMT", @"Foundation", nil, @"The source “%@” can’t be removed while it is in use.", @"User-friendly error description; first placeholder is the source’s identifier"), diff --git a/platform/darwin/src/MGLSource_Private.h b/platform/darwin/src/MGLSource_Private.h index 4a7c31694..345f25970 100644 --- a/platform/darwin/src/MGLSource_Private.h +++ b/platform/darwin/src/MGLSource_Private.h @@ -5,6 +5,8 @@ NS_ASSUME_NONNULL_BEGIN +@protocol MGLStylable; + namespace mbgl { namespace style { class Source; @@ -41,7 +43,7 @@ do { \ Initializes and returns a source with a raw pointer to the backing store, associated with a style. */ -- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource mapView:(nullable MGLMapView *)mapView; +- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource stylable:(nullable id )stylable; /** Initializes and returns a source with an owning pointer to the backing store, @@ -59,11 +61,12 @@ do { \ @property (nonatomic, readonly) mbgl::style::Source *rawSource; /** - The map view whose style currently contains the source. - If the source is not currently part of any map view’s style, this property is - set to `nil`. + The stylable object whose style currently contains the source. + + If the source is not currently part of any stylable object’s style, this + property is set to `nil`. */ -@property (nonatomic, readonly, weak) MGLMapView *mapView; +@property (nonatomic, readonly, weak) id stylable; /** Adds the mbgl source that this object represents to the mbgl map. @@ -74,7 +77,7 @@ do { \ is added back to the map via `-[MGLStyle addSource:]` and styled with a `MGLLayer`. */ -- (void)addToMapView:(MGLMapView *)mapView; +- (void)addToStylable:(id )mapView; /** Removes the mbgl source that this object represents from the mbgl map. @@ -82,7 +85,7 @@ do { \ to the `MGLSource` instance and the unique_ptr reference is valid again. It is safe to add the source back to the style after it is removed. */ -- (BOOL)removeFromMapView:(MGLMapView *)mapView error:(NSError * __nullable * __nullable)outError; +- (BOOL)removeFromStylable:(id )mapView error:(NSError * __nullable * __nullable)outError; @end diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h index ac5f74992..6c3c8d2f1 100644 --- a/platform/darwin/src/MGLStyle.h +++ b/platform/darwin/src/MGLStyle.h @@ -539,4 +539,24 @@ MGL_EXPORT @end +/** + An object whose contents are represented by an `MGLStyle` object that you + configure. + */ +@protocol MGLStylable + +/** + The style currently displayed in the receiver. + + @note The default styles provided by Mapbox contain sources and layers with + identifiers that will change over time. Applications that use APIs that + manipulate a style’s sources and layers must first set the style URL to an + explicitly versioned style using a convenience method like + `+[MGLStyle outdoorsStyleURLWithVersion:]` or a manually constructed + `NSURL`. + */ +@property (nonatomic, readonly, nullable) MGLStyle *style; + +@end + NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm index 2c3921693..6dc93ac41 100644 --- a/platform/darwin/src/MGLStyle.mm +++ b/platform/darwin/src/MGLStyle.mm @@ -77,7 +77,7 @@ const MGLExceptionName MGLRedundantSourceIdentifierException = @"MGLRedundantSou @interface MGLStyle() -@property (nonatomic, readonly, weak) MGLMapView *mapView; +@property (nonatomic, readonly, weak) id stylable; @property (nonatomic, readonly) mbgl::style::Style *rawStyle; @property (readonly, copy, nullable) NSURL *URL; @property (nonatomic, readwrite, strong) NSMutableDictionary *openGLLayers; @@ -119,14 +119,14 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, #pragma mark - -- (instancetype)initWithRawStyle:(mbgl::style::Style *)rawStyle mapView:(MGLMapView *)mapView { - MGLLogInfo(@"Initializing %@ with mapView: %@", NSStringFromClass([self class]), mapView); +- (instancetype)initWithRawStyle:(mbgl::style::Style *)rawStyle stylable:(id )stylable { + MGLLogInfo(@"Initializing %@ with stylable: %@", NSStringFromClass([self class]), stylable); if (self = [super init]) { - _mapView = mapView; + _stylable = stylable; _rawStyle = rawStyle; _openGLLayers = [NSMutableDictionary dictionary]; _localizedLayersByIdentifier = [NSMutableDictionary dictionary]; - MGLLogDebug(@"Initializing with style name: %@ mapView: %@", self.name, mapView); + MGLLogDebug(@"Initializing with style name: %@ stylable: %@", self.name, stylable); } return self; } @@ -186,17 +186,17 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, // TODO: Fill in options specific to the respective source classes // https://github.com/mapbox/mapbox-gl-native/issues/6584 if (auto vectorSource = rawSource->as()) { - return [[MGLVectorTileSource alloc] initWithRawSource:vectorSource mapView:self.mapView]; + return [[MGLVectorTileSource alloc] initWithRawSource:vectorSource stylable:self.stylable]; } else if (auto geoJSONSource = rawSource->as()) { - return [[MGLShapeSource alloc] initWithRawSource:geoJSONSource mapView:self.mapView]; + return [[MGLShapeSource alloc] initWithRawSource:geoJSONSource stylable:self.stylable]; } else if (auto rasterSource = rawSource->as()) { - return [[MGLRasterTileSource alloc] initWithRawSource:rasterSource mapView:self.mapView]; + return [[MGLRasterTileSource alloc] initWithRawSource:rasterSource stylable:self.stylable]; } else if (auto rasterDEMSource = rawSource->as()) { - return [[MGLRasterDEMSource alloc] initWithRawSource:rasterDEMSource mapView:self.mapView]; + return [[MGLRasterDEMSource alloc] initWithRawSource:rasterDEMSource stylable:self.stylable]; } else if (auto imageSource = rawSource->as()) { - return [[MGLImageSource alloc] initWithRawSource:imageSource mapView:self.mapView]; + return [[MGLImageSource alloc] initWithRawSource:imageSource stylable:self.stylable]; } else { - return [[MGLSource alloc] initWithRawSource:rawSource mapView:self.mapView]; + return [[MGLSource alloc] initWithRawSource:rawSource stylable:self.stylable]; } } @@ -211,7 +211,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, } try { - [source addToMapView:self.mapView]; + [source addToStylable:self.stylable]; } catch (std::runtime_error & err) { [NSException raise:MGLRedundantSourceIdentifierException format:@"%s", err.what()]; } @@ -243,7 +243,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, } } - return [source removeFromMapView:self.mapView error:outError]; + return [source removeFromStylable:self.stylable error:outError]; } diff --git a/platform/darwin/src/MGLStyle_Private.h b/platform/darwin/src/MGLStyle_Private.h index 1294b9ad1..360552c56 100644 --- a/platform/darwin/src/MGLStyle_Private.h +++ b/platform/darwin/src/MGLStyle_Private.h @@ -19,9 +19,9 @@ namespace mbgl { @interface MGLStyle (Private) -- (instancetype)initWithRawStyle:(mbgl::style::Style *)rawStyle mapView:(MGLMapView *)mapView; +- (instancetype)initWithRawStyle:(mbgl::style::Style *)rawStyle stylable:(id )stylable; -@property (nonatomic, readonly, weak) MGLMapView *mapView; +@property (nonatomic, readonly, weak) id stylable; @property (nonatomic, readonly) mbgl::style::Style *rawStyle; - (nullable NSArray *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor; diff --git a/platform/darwin/src/MGLVectorTileSource.mm b/platform/darwin/src/MGLVectorTileSource.mm index 53eef0efd..f9a5401b1 100644 --- a/platform/darwin/src/MGLVectorTileSource.mm +++ b/platform/darwin/src/MGLVectorTileSource.mm @@ -72,8 +72,9 @@ } std::vector features; - if (self.mapView) { - features = self.mapView.renderer->querySourceFeatures(self.rawSource->getID(), { optionalSourceLayerIDs, optionalFilter }); + if ([self.stylable isKindOfClass:[MGLMapView class]]) { + MGLMapView *mapView = (MGLMapView *)self.stylable; + features = mapView.renderer->querySourceFeatures(self.rawSource->getID(), { optionalSourceLayerIDs, optionalFilter }); } return MGLFeaturesFromMBGLFeatures(features); } diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 6161d47f0..9835d0b89 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -15,6 +15,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Snapshots +* Added the `MGLMapSnapshotter.delegate` property and `MGLMapSnapshotterDelegate` protocol for customizing the style before taking a snapshot. ([#235](https://github.com/mapbox/mapbox-gl-native-ios/pull/235)) * You no longer need to explicitly capture the `MGLMapSnapshotter` object in the completion handler that you specify in `-[MGLMapSnapshotter startWithCompletionHandler:]`. Even if you declare the snapshotter locally without holding a strong reference to it, the snapshotter is only deallocated after the completion handler finishes, and the completion handler generally receives a valid snapshot. ([#210](https://github.com/mapbox/mapbox-gl-native-ios/pull/210)) * The `-[MGLMapSnapshotter cancel]` method no longer calls the completion handler passed into `-[MGLMapSnapshotter startWithCompletionHandler:]`. ([#210](https://github.com/mapbox/mapbox-gl-native-ios/pull/210)) * Fixed an issue where the `MGLMapSnapshotter.loading` property always returned `NO`, even while loading a snapshot. ([#210](https://github.com/mapbox/mapbox-gl-native-ios/pull/210)) diff --git a/platform/ios/src/MGLMapView+Impl.mm b/platform/ios/src/MGLMapView+Impl.mm index fbe35ff71..7d8a69ab1 100644 --- a/platform/ios/src/MGLMapView+Impl.mm +++ b/platform/ios/src/MGLMapView+Impl.mm @@ -50,7 +50,7 @@ void MGLMapViewImpl::onDidFailLoadingMap(mbgl::MapLoadError mapError, const std: break; case mbgl::MapLoadError::StyleLoadError: code = MGLErrorCodeLoadStyleFailed; - description = NSLocalizedStringWithDefaultValue(@"LOAD_STYLE_FAILED_DESC", nil, nil, @"The map failed to load because the style can't be loaded.", @"User-friendly error description"); + description = NSLocalizedStringWithDefaultValue(@"LOAD_STYLE_FAILED_DESC", nil, nil, @"The map failed to load because the style can’t be loaded.", @"User-friendly error description"); break; case mbgl::MapLoadError::NotFoundError: code = MGLErrorCodeNotFound; diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index dd13ebe06..a131deea7 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -5,6 +5,7 @@ #import "MGLGeometry.h" #import "MGLMapCamera.h" #import "MGLTypes.h" +#import "MGLStyle.h" NS_ASSUME_NONNULL_BEGIN @@ -14,7 +15,6 @@ NS_ASSUME_NONNULL_BEGIN @class MGLPolyline; @class MGLPolygon; @class MGLShape; -@class MGLStyle; @protocol MGLMapViewDelegate; @protocol MGLAnnotation; @@ -188,7 +188,7 @@ FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLUserLocationAnnotationTyp Simple map view example to learn how to initialize a basic `MGLMapView`. */ MGL_EXPORT -@interface MGLMapView : UIView +@interface MGLMapView : UIView #pragma mark Creating Instances diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 69db87194..5bf5ce756 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -6459,7 +6459,7 @@ public: return; } - self.style = [[MGLStyle alloc] initWithRawStyle:&self.mbglMap.getStyle() mapView:self]; + self.style = [[MGLStyle alloc] initWithRawStyle:&self.mbglMap.getStyle() stylable:self]; if ([self.delegate respondsToSelector:@selector(mapView:didFinishLoadingStyle:)]) { [self.delegate mapView:self didFinishLoadingStyle:self.style]; diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 9aed9fdcf..873f382c1 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -46,6 +46,7 @@ ### Snapshots * Added an `-[MGLMapSnapshotter startWithOverlayHandler:completionHandler:]` method to provide the snapshot's current `CGContext` in order to perform custom drawing on `MGLMapSnapshot` objects. ([#15530](https://github.com/mapbox/mapbox-gl-native/pull/15530)) +* Added the `MGLMapSnapshotter.delegate` property and `MGLMapSnapshotterDelegate` protocol for customizing the style before taking a snapshot. ([#235](https://github.com/mapbox/mapbox-gl-native-ios/pull/235)) * You no longer need to explicitly capture the `MGLMapSnapshotter` object in the completion handler that you specify in `-[MGLMapSnapshotter startWithCompletionHandler:]`. Even if you declare the snapshotter locally without holding a strong reference to it, the snapshotter is only deallocated after the completion handler finishes, and the completion handler generally receives a valid snapshot. ([#210](https://github.com/mapbox/mapbox-gl-native-ios/pull/210)) * The `-[MGLMapSnapshotter cancel]` method no longer calls the completion handler passed into `-[MGLMapSnapshotter startWithCompletionHandler:]`. ([#210](https://github.com/mapbox/mapbox-gl-native-ios/pull/210)) * Fixed an issue where the `MGLMapSnapshotter.loading` property always returned `NO`, even while loading a snapshot. ([#210](https://github.com/mapbox/mapbox-gl-native-ios/pull/210)) diff --git a/platform/macos/src/MGLMapView.h b/platform/macos/src/MGLMapView.h index d00383518..69e397b48 100644 --- a/platform/macos/src/MGLMapView.h +++ b/platform/macos/src/MGLMapView.h @@ -4,12 +4,12 @@ #import "MGLFoundation.h" #import "MGLTypes.h" #import "MGLGeometry.h" +#import "MGLStyle.h" NS_ASSUME_NONNULL_BEGIN @class MGLAnnotationImage; @class MGLMapCamera; -@class MGLStyle; @class MGLShape; @protocol MGLAnnotation; @@ -65,7 +65,7 @@ NS_ASSUME_NONNULL_BEGIN ensuring that your use adheres to the relevant terms of use. */ MGL_EXPORT IB_DESIGNABLE -@interface MGLMapView : NSView +@interface MGLMapView : NSView #pragma mark Creating Instances diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index ece95e179..8d1d91f02 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -961,7 +961,7 @@ public: return; } - self.style = [[MGLStyle alloc] initWithRawStyle:&_mbglMap->getStyle() mapView:self]; + self.style = [[MGLStyle alloc] initWithRawStyle:&_mbglMap->getStyle() stylable:self]; if ([self.delegate respondsToSelector:@selector(mapView:didFinishLoadingStyle:)]) { [self.delegate mapView:self didFinishLoadingStyle:self.style];