Cherry pick changes from mapbox-gl-native-ios. resolves #29

This commit is contained in:
Petr Pokorny
2021-02-22 13:01:41 +01:00
parent 62c6178029
commit 80e98fd953
24 changed files with 999 additions and 54 deletions

View File

@@ -0,0 +1,41 @@
#import "MGLFoundation.h"
NS_ASSUME_NONNULL_BEGIN
/**
Type of event used when subscribing to and unsubscribing from an `MGLObservable`.
*/
typedef NSString *MGLEventType NS_TYPED_EXTENSIBLE_ENUM;
// TODO: Doc
FOUNDATION_EXPORT MGL_EXPORT MGLEventType const MGLEventTypeResourceRequest;
/**
Generic Event used when notifying an `MGLObserver`. This is not intended nor
expected to be created by the application developer. It will be provided as
part of an `MGLObservable` notification.
*/
MGL_EXPORT
@interface MGLEvent: NSObject
/// Type of an event. Matches an original event type used for a subscription.
@property (nonatomic, readonly, copy) MGLEventType type;
/// Timestamp taken at the time of an event creation, relative to the Unix epoch.
@property (nonatomic, readonly) NSTimeInterval begin;
/// Timestamp taken at the time of an event completion. For a non-interval
/// (single-shot) events, migth be equal to an event's `begin` timestamp.
/// Relative to the Unix epoch.
@property (nonatomic, readonly) NSTimeInterval end;
/// Generic property for the event's data. Supported types are: `NSNumber` (int64,
/// uint64, bool, double), `NSString`, `NSArray`, `NSDictionary`.
@property (nonatomic, readonly, copy) id data;
/// Test for equality. Note this compares all properties except `data`.
- (BOOL)isEqualToEvent:(MGLEvent *)otherEvent;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,80 @@
#import "MGLEvent_Private.h"
#import "MGLStyleValue_Private.h"
const MGLEventType MGLEventTypeResourceRequest = @"resource-request";
#pragma mark - Event
@implementation MGLEvent
- (instancetype)init {
[self doesNotRecognizeSelector:_cmd];
return nil;
}
- (instancetype)initWithEvent:(const mbgl::ObservableEvent&)event {
self = [super init];
if (!self)
return nil;
// mbgl::ObservableEvent timestamps do not use the system_clock, i.e. they
// are not relative to the Unix epoch, so we'll need to offset appropriately
auto systemClockNow = std::chrono::system_clock::now();
auto steadyClockNow = std::chrono::steady_clock::now();
auto begin = std::chrono::time_point_cast<std::chrono::system_clock::duration>(systemClockNow +
(event.begin - steadyClockNow));
auto end = std::chrono::time_point_cast<std::chrono::system_clock::duration>(systemClockNow +
(event.end - steadyClockNow));
auto beginTime = std::chrono::duration<double, std::ratio<1>>(begin.time_since_epoch()).count();
auto endTime = std::chrono::duration<double, std::ratio<1>>(end.time_since_epoch()).count();
_type = (MGLEventType)[NSString stringWithUTF8String:event.type.c_str()];
_begin = beginTime;
_end = endTime;
// From value.md
// Supported types are `int`, `uint`, `bool`, `double`, `array`, `object` and `string`.
_data = MGLJSONObjectFromMBGLValue(event.data);
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p; type = %@, begin = %f, end = %f, data = %@>",
NSStringFromClass([self class]), (void *)self,
self.type,
self.begin,
self.end,
self.data];
}
#pragma mark - Equality
- (NSUInteger)hash {
return self.type.hash ^ @(self.begin).hash ^ @(self.end).hash;
}
- (BOOL)isEqualToEvent:(MGLEvent *)other {
if (self == other)
return YES;
// Ignore the value at this moment.
return ((self.type == other.type) &&
(self.begin == other.begin) &&
(self.end == other.end));
}
- (BOOL)isEqual:(id)other {
if (self == other)
return YES;
if (![other isKindOfClass:[MGLEvent class]]) {
return NO;
}
return [self isEqualToEvent:(MGLEvent*)other];
}
@end

View File

@@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
#import "MGLEvent.h"
#include <mbgl/util/observable.hpp>
NS_ASSUME_NONNULL_BEGIN
@interface MGLEvent ()
- (instancetype)initWithEvent:(const mbgl::ObservableEvent&)event;
@end
NS_ASSUME_NONNULL_END

View File

@@ -58,6 +58,18 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)setDesiredAccuracy:(CLLocationAccuracy)desiredAccuracy;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000
/**
Specifies the level of location accuracy the Maps SDK has permission to use.
@note If the value of this property is `CLAccuracyAuthorizationFullAccuracy`, you can set the
`MGLLocationManager.desiredAccuracy` property to any value. If the value is `CLAccuracyAuthorizationReducedAccuracy`,
setting `MGLLocationManager.desiredAccuracy` to a value other than` kCLLocationAccuracyReduced` has no effect on
the location information.
*/
- (CLAccuracyAuthorization)accuracyAuthorization API_AVAILABLE(ios(14));
#endif
/**
Specifies the type of user activity associated with the location updates.
@@ -78,6 +90,17 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)setActivityType:(CLActivityType)activityType;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000
/**
Requests the user's permission to temporarily use location update services
with full accuracy.
@note If the user turned off location accuracy you may use this method to
request full accuracy for a session.
*/
- (void)requestTemporaryFullAccuracyAuthorizationWithPurposeKey:(NSString *)purposeKey API_AVAILABLE(ios(14));
#endif
@required
/**
@@ -195,6 +218,14 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)locationManager:(id<MGLLocationManager>)manager
didFailWithError:(nonnull NSError *)error;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000
/**
Notifies the delegate that the location authorization status has changed.
@param manager The location manager reporting the change.
*/
- (void)locationManagerDidChangeAuthorization:(id<MGLLocationManager>)manager;
#endif
@optional

View File

@@ -38,7 +38,16 @@
}
- (CLAuthorizationStatus)authorizationStatus {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000
if (@available(iOS 14.0, *)) {
return self.locationManager.authorizationStatus;
} else {
return kCLAuthorizationStatusNotDetermined;
}
#else
return [CLLocationManager authorizationStatus];
return [CLLocationManager authorizationStatus];
#endif
}
- (void)setActivityType:(CLActivityType)activityType {
@@ -49,6 +58,23 @@
return self.locationManager.activityType;
}
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000
- (CLAccuracyAuthorization)accuracyAuthorization {
if (@available(iOS 14.0, *)) {
return self.locationManager.accuracyAuthorization;
} else {
return CLAccuracyAuthorizationFullAccuracy;
}
}
- (void)requestTemporaryFullAccuracyAuthorizationWithPurposeKey:(NSString *)purposeKey {
if (@available(iOS 14.0, *)) {
[self.locationManager requestTemporaryFullAccuracyAuthorizationWithPurposeKey:purposeKey];
}
}
#endif
- (void)dismissHeadingCalibrationDisplay {
[self.locationManager dismissHeadingCalibrationDisplay];
}
@@ -113,4 +139,9 @@
}
}
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000
- (void)locationManagerDidChangeAuthorization:(CLLocationManager *)manager {
[self.delegate locationManagerDidChangeAuthorization:self];
}
#endif
@end

View File

@@ -0,0 +1,65 @@
#import "MGLFoundation.h"
#import "MGLEvent.h"
NS_ASSUME_NONNULL_BEGIN
#pragma mark -
/**
Base class for observers used to receive notifications from an `MGLObservable`.
Subclasses should only override `-[MGLObserver notifyWithEvent:]`.
*/
MGL_EXPORT
@interface MGLObserver: NSObject
/// A unique identifier that can be used to identify an observer.
@property (nonatomic, readonly) NSUInteger identifier;
/// Primary notification method.
- (void)notifyWithEvent:(MGLEvent*)event;
// TODO: document
- (BOOL)isEqualToObserver:(MGLObserver *)other;
@end
#pragma mark -
/**
Protocol that yypes that wish to support subscriptions for events should conform
to.
*/
MGL_EXPORT
@protocol MGLObservable
/**
Subscribes an `MGLObserver` to a provided set of event types.
`MGLObservable` will hold a strong reference to an `MGLObserver` instance,
therefore, in order to stop receiving notifications (and to avoid memory leaks),
the caller must call unsubscribe with the `MGLObserver` instance used for
the initial subscription.
@param observer an MGLObserver
@param events a set of event types to subscribe to.
*/
- (void)subscribeForObserver:(nonnull MGLObserver *)observer
events:(nonnull NSSet<MGLEventType> *)events;
/**
Unsubscribes an `MGLObserver` from a provided set of event types.
@param observer an MGLObserver
@param events a set of event types to unsubscribe from.
*/
- (void)unsubscribeForObserver:(nonnull MGLObserver *)observer
events:(nonnull NSSet<MGLEventType> *)events;
/**
Unsubscribes an `MGLObserver` from all events (and release the strong reference).
@param observer an MGLObserver
*/
- (void)unsubscribeForObserver:(nonnull MGLObserver *)observer;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,101 @@
#import "MGLObserver_Private.h"
#import "MGLLoggingConfiguration_Private.h"
#import "MGLEvent_Private.h"
#pragma mark - Native C++ peer object
namespace mbgl {
namespace darwin {
void Observer::notify(const ObservableEvent& event) {
if (!observer) {
MGLLogWarning(@"Platform observer has been deallocated");
return;
}
[observer notifyWithEvent:[[MGLEvent alloc] initWithEvent:event]];
}
std::size_t Observer::id() const {
if (!observer) {
MGLLogWarning(@"Platform observer has been deallocated");
return 0;
}
return static_cast<std::size_t>(observer.identifier);
}
}
}
#pragma mark - Cocoa observer
@implementation MGLObserver
+ (NSUInteger)nextIdentifier {
static NSUInteger identifier;
return ++identifier;
}
- (void)dealloc {
MGLAssert(!_observing, @"Ensure the observer is unsubscribed before deallocating");
}
- (instancetype)init {
self = [super init];
if (!self) return nil;
_identifier = [MGLObserver nextIdentifier];
_peer = std::make_shared<mbgl::darwin::Observer>(self);
return self;
}
- (void)notifyWithEvent:(MGLEvent*)event {
if (self.notificationHandler) {
self.notificationHandler(event);
}
}
- (BOOL)isEqualToObserver:(MGLObserver *)other {
if (self == other)
return YES;
if (self.identifier != other.identifier)
return NO;
return (self.peer == other.peer);
}
- (BOOL)isEqual:(id)other {
if (self == other)
return YES;
if (![other isKindOfClass:[MGLObserver class]]) {
return NO;
}
return [self isEqualToObserver:(MGLObserver*)other];
}
- (NSUInteger)hash {
// Rotate the address
NSUInteger peerHash = reinterpret_cast<NSUInteger>(self.peer.get());
NSUInteger width = (sizeof(NSUInteger) * __CHAR_BIT__);
NSUInteger shift = self.identifier % width;
NSUInteger newHash = (peerHash << shift) | (peerHash >> (width - shift));
return newHash;
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p; identifier = %lu, peer = %p, hash = %lu>",
NSStringFromClass([self class]), (void *)self,
(unsigned long)self.identifier,
self.peer.get(),
(unsigned long)[self hash]];
}
@end

View File

@@ -0,0 +1,40 @@
#import <mbgl/util/observable.hpp>
#import "MGLObserver.h"
NS_ASSUME_NONNULL_BEGIN
#pragma mark - Native C++ peer object
namespace mbgl {
namespace darwin {
class Observer : public mbgl::Observer {
public:
Observer(MGLObserver *observer_): observer(observer_) {}
virtual ~Observer() = default;
virtual void notify(const ObservableEvent& event);
virtual std::size_t id() const;
protected:
/// Cocoa observer that this adapter bridges to.
__weak MGLObserver *observer = nullptr;
};
}
}
#pragma mark -
@interface MGLObserver ()
@property (nonatomic, assign) std::shared_ptr<mbgl::darwin::Observer> peer;
// TODO: Consider making public
@property (nonatomic, copy) void (^notificationHandler)(MGLEvent *);
// Debug property used for development.
@property (nonatomic, assign) BOOL observing;
@end
NS_ASSUME_NONNULL_END

View File

@@ -14,6 +14,8 @@ id MGLJSONObjectFromMBGLValue(const mbgl::Value &value) {
return @(value);
}, [](const int64_t value) {
return @(value);
}, [](const uint64_t value) {
return @(value);
}, [](const double value) {
return @(value);
}, [](const std::string &value) {

View File

@@ -35,6 +35,8 @@ namespace mbgl {
}
}
id MGLJSONObjectFromMBGLValue(const mbgl::Value &value);
NS_INLINE MGLTransition MGLTransitionFromOptions(const mbgl::style::TransitionOptions& options) {
MGLTransition transition;
transition.duration = MGLTimeIntervalFromDuration(options.duration.value_or(mbgl::Duration::zero()));

View File

@@ -6,6 +6,19 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
### Features
* Added `MGLLocationManager.accuracyAuthorization` and `[MGLLocationManager requestTemporaryFullAccuracyAuthorizationWithPurposeKeyproperty:]` to support iOS 14 location accuracy privacy changes. (cherry pick from [#361](https://github.com/mapbox/mapbox-gl-native-ios/pull/361))
* Added `[MGLLocationManager requestTemporaryFullAccuracyAuthorizationWithPurposeKeyproperty:]` to allow developers request just-in-time full-accuracy permissions. (cherry pick from [#361](https://github.com/mapbox/mapbox-gl-native-ios/pull/361))
* Added `[MGLLocationManagerDelegate locationManagerDidChangeAuthorization:]` to let `MGLMapView` know about privacy changes. (cherry pick from [#376](https://github.com/mapbox/mapbox-gl-native-ios/pull/376))
* Added `[MGLMapViewDelegate mapView:didChangeLocationManagerAuthorization:]` to allow developers adjust their apps to privacy settings changes. (cherry pick from [#376](https://github.com/mapbox/mapbox-gl-native-ios/pull/376))
* Added `MGLObserver`, `MGLObservable` and `MGLEvent` to monitor events from the map view. (cherry pick from [#358](https://github.com/mapbox/mapbox-gl-native-ios/pull/358))
* Added an approximate user location halo when `MGLLocationManager.accuracyAuthorization` is set to `CLAccuracyAuthorizationReducedAccuracy`. (cherry pick from [#381](https://github.com/mapbox/mapbox-gl-native-ios/pull/381))
* The `MGLAccuracyAuthorizationDescription` as element of `NSLocationTemporaryUsageDescriptionDictionary` Info.plist key can now be set to describe why you request accuracy authorization. (cherry pick from [#392](https://github.com/mapbox/mapbox-gl-native-ios/pull/392))
* Added `[MGLMapViewDelegate mapViewStyleForDefaultUserLocationAnnotationView:]` and `MGLUserLocationAnnotationViewStyle` class to allow developers customize the default user location annotation view UI style. (cherry pick from [#403](https://github.com/mapbox/mapbox-gl-native-ios/pull/403))
### Bug Fixes
* Fixed an issue that caused a crash when custom location managers did not implement `MGLLocationManager.accuracyAuthorization`. (cherry pick from [#474](https://github.com/mapbox/mapbox-gl-native-ios/pull/474))
### Other
* mapbox-gl-js submodule has been replaced with maplibre-gl-js

View File

@@ -24,6 +24,11 @@
<string>15256</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
<key>MGLAccuracyAuthorizationDescription</key>
<string>MapLibre requires your precise location to help you navigate the map.</string>
</dict>
<key>NSHumanReadableCopyright</key>
<string>© 2014–2019 Mapbox</string>
<key>NSLocationAlwaysUsageDescription</key>

View File

@@ -2,7 +2,7 @@
#import "MBXOrnamentsViewController.h"
@interface MBXOrnamentsViewController ()<MGLMapViewDelegate>
@interface MBXOrnamentsViewController ()
@property (nonatomic) MGLMapView *mapView;
@property (nonatomic) NSTimer *timer;
@@ -64,7 +64,6 @@
zoomLevel:16
direction:30
animated:NO];
mapView.delegate = self;
mapView.showsScale = YES;
[self.view addSubview:mapView];

View File

@@ -15,7 +15,6 @@
#import "MBXFrameTimeGraphView.h"
#import "../src/MGLMapView_Experimental.h"
#import <objc/runtime.h>
static const CLLocationCoordinate2D WorldTourDestinations[] = {
@@ -193,6 +192,15 @@ CLLocationCoordinate2D randomWorldCoordinate() {
@implementation MBXSpriteBackedAnnotation
@end
@interface MBXTestObserver: MGLObserver
@end
@implementation MBXTestObserver
- (void)notifyWithEvent:(MGLEvent *)event {
NSLog(@"Received event: %@", event);
}
@end
@interface MBXViewController () <UITableViewDelegate,
UITableViewDataSource,
MGLMapViewDelegate,
@@ -213,7 +221,8 @@ CLLocationCoordinate2D randomWorldCoordinate() {
@property (nonatomic) BOOL zoomLevelOrnamentEnabled;
@property (nonatomic) NSMutableArray<UIWindow *> *helperWindows;
@property (nonatomic) NSMutableArray<UIView *> *contentInsetsOverlays;
@property (nonatomic) MBXTestObserver *testObserver;
@property (nonatomic, copy) void (^locationBlock)(void);
@end
@interface MGLMapView (MBXViewController)
@@ -300,6 +309,15 @@ CLLocationCoordinate2D randomWorldCoordinate() {
}
}
}];
// TODO: Replace with menu implementation
self.testObserver = [[MBXTestObserver alloc] init];
[self.mapView subscribeForObserver:self.testObserver event:MGLEventTypeResourceRequest];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.mapView unsubscribeForObserver:self.testObserver];
self.testObserver = nil;
});
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
@@ -1952,6 +1970,12 @@ CLLocationCoordinate2D randomWorldCoordinate() {
}
- (IBAction)locateUser:(id)sender
{
[self nextTrackingMode:sender];
}
- (void)nextTrackingMode:(id)sender
{
MGLUserTrackingMode nextMode;
NSString *nextAccessibilityValue;
@@ -2333,6 +2357,34 @@ CLLocationCoordinate2D randomWorldCoordinate() {
}
}
- (void)mapView:(nonnull MGLMapView *)mapView didChangeLocationManagerAuthorization:(nonnull id<MGLLocationManager>)manager {
if (@available(iOS 14, *)) {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000
if (manager.authorizationStatus == kCLAuthorizationStatusDenied || manager.accuracyAuthorization == CLAccuracyAuthorizationReducedAccuracy) {
[self alertAccuracyChanges];
}
#endif
}
}
- (void)alertAccuracyChanges {
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Mapbox GL works best with your precise location."
message:@"You'll get turn-by-turn directions."
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:@"Turn On in Settings" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
}];
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"Keep Precise Location Off" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
handler:nil];
[alert addAction:settingsAction];
[alert addAction:defaultAction];
[self presentViewController:alert animated:YES completion:nil];
}
- (void)saveCurrentMapState:(__unused NSNotification *)notification {
// The following properties can change after the view loads so we need to save their

View File

@@ -10,6 +10,22 @@ Mapbox-hosted vector tiles and styles require an API access token, which you can
As an alternative, you can use `MGLAccountManager.accessToken` to set a token in code. See [our guide](https://www.mapbox.com/help/ios-private-access-token/) for some tips on keeping access tokens in open source code private.
## MGLAccuracyAuthorizationDescription
Set the Mapbox accuracy authorization description string as an element of `NSLocationTemporaryUsageDescriptionDictionary` to be used by the map to request authorization when the `MGLLocationManager.accuracyAuthorization` is set to `CLAccuracyAuthorizationReducedAccuracy`. Requesting accuracy authorization is available for devices running iOS 14.0 and above.
Example:
```xml
<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
<key>MGLAccuracyAuthorizationDescription</key>
<string>Mapbox requires your precise location to help you navigate the map.</string>
</dict>
```
Remove `MGLAccuracyAuthorizationDescription` if you want to control when to request for accuracy authorization.
## MGLMapboxAPIBaseURL
Use this key if you need to customize the API base URL used throughout the SDK. If unset, the default Mapbox API is used.
@@ -47,4 +63,3 @@ At runtime, you can obtain the value of this key using the `MGLOfflineStorage.da
Beginning in version 4.0, the SDK also performs collision detection between style layers based on different sources by default. For the default behavior, omit the `MGLCollisionBehaviorPre4_0` key or set it to NO (`false`).
This property may also be set using `[[NSUserDefaults standardUserDefaults] setObject:@(YES) forKey:@"MGLCollisionBehaviorPre4_0"]`; it will override any value specified in the `Info.plist`.

View File

@@ -224,6 +224,22 @@
40F887701D7A1E58008ECB67 /* MGLShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */; };
40F887711D7A1E59008ECB67 /* MGLShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */; };
40FDA76B1CCAAA6800442548 /* MBXAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */; };
4BC5767B25E3C8AB006E06EB /* MGLObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC5767525E3C8AA006E06EB /* MGLObserver.h */; };
4BC5767C25E3C8AB006E06EB /* MGLObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC5767525E3C8AA006E06EB /* MGLObserver.h */; };
4BC5767D25E3C8AB006E06EB /* MGLEvent_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC5767625E3C8AB006E06EB /* MGLEvent_Private.h */; };
4BC5767E25E3C8AB006E06EB /* MGLEvent_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC5767625E3C8AB006E06EB /* MGLEvent_Private.h */; };
4BC5767F25E3C8AB006E06EB /* MGLEvent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5767725E3C8AB006E06EB /* MGLEvent.mm */; };
4BC5768025E3C8AB006E06EB /* MGLEvent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5767725E3C8AB006E06EB /* MGLEvent.mm */; };
4BC5768125E3C8AB006E06EB /* MGLObserver.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5767825E3C8AB006E06EB /* MGLObserver.mm */; };
4BC5768225E3C8AB006E06EB /* MGLObserver.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5767825E3C8AB006E06EB /* MGLObserver.mm */; };
4BC5768325E3C8AB006E06EB /* MGLEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC5767925E3C8AB006E06EB /* MGLEvent.h */; };
4BC5768425E3C8AB006E06EB /* MGLEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC5767925E3C8AB006E06EB /* MGLEvent.h */; };
4BC5768525E3C8AB006E06EB /* MGLObserver_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC5767A25E3C8AB006E06EB /* MGLObserver_Private.h */; };
4BC5768625E3C8AB006E06EB /* MGLObserver_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC5767A25E3C8AB006E06EB /* MGLObserver_Private.h */; };
4BC5769325E3D3D6006E06EB /* MGLUserLocationAnnotationViewStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC5769125E3D3D6006E06EB /* MGLUserLocationAnnotationViewStyle.h */; };
4BC5769425E3D3D6006E06EB /* MGLUserLocationAnnotationViewStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC5769125E3D3D6006E06EB /* MGLUserLocationAnnotationViewStyle.h */; };
4BC5769525E3D3D6006E06EB /* MGLUserLocationAnnotationViewStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5769225E3D3D6006E06EB /* MGLUserLocationAnnotationViewStyle.m */; };
4BC5769625E3D3D6006E06EB /* MGLUserLocationAnnotationViewStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5769225E3D3D6006E06EB /* MGLUserLocationAnnotationViewStyle.m */; };
550570C622958FB400228ECF /* MGLMapView+Impl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 550570C422958FB300228ECF /* MGLMapView+Impl.mm */; };
550570C722958FB400228ECF /* MGLMapView+Impl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 550570C422958FB300228ECF /* MGLMapView+Impl.mm */; };
550570C822958FB400228ECF /* MGLMapView+Impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 550570C522958FB400228ECF /* MGLMapView+Impl.h */; };
@@ -907,6 +923,14 @@
40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeSource_Private.h; sourceTree = "<group>"; };
40FDA7691CCAAA6800442548 /* MBXAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXAnnotationView.h; sourceTree = "<group>"; };
40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXAnnotationView.m; sourceTree = "<group>"; };
4BC5767525E3C8AA006E06EB /* MGLObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLObserver.h; sourceTree = "<group>"; };
4BC5767625E3C8AB006E06EB /* MGLEvent_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLEvent_Private.h; sourceTree = "<group>"; };
4BC5767725E3C8AB006E06EB /* MGLEvent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLEvent.mm; sourceTree = "<group>"; };
4BC5767825E3C8AB006E06EB /* MGLObserver.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLObserver.mm; sourceTree = "<group>"; };
4BC5767925E3C8AB006E06EB /* MGLEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLEvent.h; sourceTree = "<group>"; };
4BC5767A25E3C8AB006E06EB /* MGLObserver_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLObserver_Private.h; sourceTree = "<group>"; };
4BC5769125E3D3D6006E06EB /* MGLUserLocationAnnotationViewStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationAnnotationViewStyle.h; sourceTree = "<group>"; };
4BC5769225E3D3D6006E06EB /* MGLUserLocationAnnotationViewStyle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLUserLocationAnnotationViewStyle.m; sourceTree = "<group>"; };
550570C422958FB300228ECF /* MGLMapView+Impl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "MGLMapView+Impl.mm"; sourceTree = "<group>"; };
550570C522958FB400228ECF /* MGLMapView+Impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLMapView+Impl.h"; sourceTree = "<group>"; };
554180411D2E97DE00012372 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; };
@@ -1894,6 +1918,12 @@
1F6A82A021360F9C00BA5B41 /* MGLLoggingConfiguration.h */,
CF75A91422D85E860058A5C4 /* MGLLoggingConfiguration.mm */,
1F6A82A62138871900BA5B41 /* MGLLoggingConfiguration_Private.h */,
4BC5767625E3C8AB006E06EB /* MGLEvent_Private.h */,
4BC5767925E3C8AB006E06EB /* MGLEvent.h */,
4BC5767725E3C8AB006E06EB /* MGLEvent.mm */,
4BC5767A25E3C8AB006E06EB /* MGLObserver_Private.h */,
4BC5767525E3C8AA006E06EB /* MGLObserver.h */,
4BC5767825E3C8AB006E06EB /* MGLObserver.mm */,
);
name = Foundation;
path = ../darwin/src;
@@ -1919,6 +1949,8 @@
5580B45A229570A10091291B /* MGLMapView+OpenGL.mm */,
DA8848371CBAFB8500AB86E3 /* MGLMapView+IBAdditions.h */,
DA737EE01D056A4E005BDA16 /* MGLMapViewDelegate.h */,
4BC5769125E3D3D6006E06EB /* MGLUserLocationAnnotationViewStyle.h */,
4BC5769225E3D3D6006E06EB /* MGLUserLocationAnnotationViewStyle.m */,
);
name = Kit;
path = src;
@@ -2182,6 +2214,7 @@
357FE2DD1E02D2B20068B753 /* NSCoder+MGLAdditions.h in Headers */,
35D13AB71D3D15E300AFB4E0 /* MGLStyleLayer.h in Headers */,
07D947531F67488E00E37934 /* MGLComputedShapeSource_Private.h in Headers */,
4BC5768325E3C8AB006E06EB /* MGLEvent.h in Headers */,
9654C1261FFC1AB900DB6A19 /* MGLPolyline_Private.h in Headers */,
40F887701D7A1E58008ECB67 /* MGLShapeSource_Private.h in Headers */,
350098DC1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h in Headers */,
@@ -2195,6 +2228,7 @@
3510FFF01D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */,
74CB5EBF219B280400102936 /* MGLHeatmapStyleLayer_Private.h in Headers */,
1FC4817D2098CBC0000D09B4 /* NSPredicate+MGLPrivateAdditions.h in Headers */,
4BC5767B25E3C8AB006E06EB /* MGLObserver.h in Headers */,
1F2B94C0221636D900210640 /* MGLNetworkConfiguration_Private.h in Headers */,
353AFA141D65AB17005A69F4 /* NSDate+MGLAdditions.h in Headers */,
DA8848531CBAFB9800AB86E3 /* MGLCompactCalloutView.h in Headers */,
@@ -2239,6 +2273,7 @@
CAFB3C14234505D500399265 /* MGLMapSnapshotter_Private.h in Headers */,
1FCAE2A220B872A400C577DD /* MGLLocationManager.h in Headers */,
DACA86262019218600E9693A /* MGLRasterDEMSource.h in Headers */,
4BC5769325E3D3D6006E06EB /* MGLUserLocationAnnotationViewStyle.h in Headers */,
353933F81D3FB79F003F57D7 /* MGLLineStyleLayer.h in Headers */,
92F2C3ED1F0E3C3A00268EC0 /* MGLRendererFrontend.h in Headers */,
DAAF722D1DA903C700312FA4 /* MGLStyleValue_Private.h in Headers */,
@@ -2300,6 +2335,7 @@
DA72620B1DEEE3480043BB89 /* MGLOpenGLStyleLayer.h in Headers */,
404C26E71D89C55D000AA13D /* MGLTileSource_Private.h in Headers */,
DA88485C1CBAFB9800AB86E3 /* MGLFaux3DUserLocationAnnotationView.h in Headers */,
4BC5767D25E3C8AB006E06EB /* MGLEvent_Private.h in Headers */,
359F57461D2FDDA6005217F1 /* MGLUserLocationAnnotationView_Private.h in Headers */,
404C26E21D89B877000AA13D /* MGLTileSource.h in Headers */,
DA8847FD1CBAFA5100AB86E3 /* MGLTilePyramidOfflineRegion.h in Headers */,
@@ -2308,6 +2344,7 @@
350098BB1D480108004B2AF0 /* MGLVectorTileSource.h in Headers */,
DA8847F61CBAFA5100AB86E3 /* MGLOfflineStorage.h in Headers */,
DAD1656E1CF41981001FF4B9 /* MGLFeature_Private.h in Headers */,
4BC5768525E3C8AB006E06EB /* MGLObserver_Private.h in Headers */,
DA88483C1CBAFB8500AB86E3 /* MGLMapView.h in Headers */,
3EA9363147E77DD29FA06063 /* MGLRendererConfiguration.h in Headers */,
550570C822958FB400228ECF /* MGLMapView+Impl.h in Headers */,
@@ -2340,6 +2377,7 @@
3566C7671D4A77BA008152BC /* MGLShapeSource.h in Headers */,
DA35A29F1CC9E94C00E826B2 /* MGLCoordinateFormatter.h in Headers */,
967C864C210A9D3C004DF794 /* UIDevice+MGLAdditions.h in Headers */,
4BC5768425E3C8AB006E06EB /* MGLEvent.h in Headers */,
404C26E31D89B877000AA13D /* MGLTileSource.h in Headers */,
96E516F6200059EC00A02306 /* MGLRendererFrontend.h in Headers */,
071BBB041EE76147001FB02A /* MGLImageSource.h in Headers */,
@@ -2353,6 +2391,7 @@
96E516EF2000594F00A02306 /* NSArray+MGLAdditions.h in Headers */,
96E516F12000596800A02306 /* NSString+MGLAdditions.h in Headers */,
35E0CFE71D3E501500188327 /* MGLStyle_Private.h in Headers */,
4BC5767C25E3C8AB006E06EB /* MGLObserver.h in Headers */,
CA55CD42202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */,
DABFB86D1CBE9A0F00D62B32 /* MGLAnnotationImage.h in Headers */,
DABFB8721CBE9A0F00D62B32 /* MGLUserLocation.h in Headers */,
@@ -2397,6 +2436,7 @@
1F2B94C1221636D900210640 /* MGLNetworkConfiguration_Private.h in Headers */,
35CE61831D4165D9004F2359 /* UIColor+MGLAdditions.h in Headers */,
96E516F32000597100A02306 /* NSDictionary+MGLAdditions.h in Headers */,
4BC5769425E3D3D6006E06EB /* MGLUserLocationAnnotationViewStyle.h in Headers */,
1FCAE2A920B88B3800C577DD /* MGLLocationManager_Private.h in Headers */,
96E516F02000595800A02306 /* NSBundle+MGLAdditions.h in Headers */,
96E516F920005A3500A02306 /* MGLFaux3DUserLocationAnnotationView.h in Headers */,
@@ -2458,6 +2498,7 @@
96E516E82000560B00A02306 /* MGLAnnotationContainerView_Private.h in Headers */,
35D13AC41D3D19DD00AFB4E0 /* MGLFillStyleLayer.h in Headers */,
DABFB86E1CBE9A0F00D62B32 /* MGLCalloutView.h in Headers */,
4BC5767E25E3C8AB006E06EB /* MGLEvent_Private.h in Headers */,
96E516FC20005A4400A02306 /* MGLUserLocationHeadingIndicator.h in Headers */,
1F7454971ECD450D00021D39 /* MGLLight_Private.h in Headers */,
DABFB8601CBE99E500D62B32 /* MGLMapCamera.h in Headers */,
@@ -2466,6 +2507,7 @@
DABFB86A1CBE99E500D62B32 /* MGLStyle.h in Headers */,
DA00FC8F1D5EEB0D009AABC8 /* MGLAttributionInfo.h in Headers */,
96E516E12000551100A02306 /* MGLMultiPoint_Private.h in Headers */,
4BC5768625E3C8AB006E06EB /* MGLObserver_Private.h in Headers */,
3EA934623AD0000B7D99C3FB /* MGLRendererConfiguration.h in Headers */,
DACA86272019218600E9693A /* MGLRasterDEMSource.h in Headers */,
9621F2502091020E005B3800 /* NSExpression+MGLAdditions.h in Headers */,
@@ -3055,6 +3097,7 @@
DA8848261CBAFA6200AB86E3 /* MGLPolygon.mm in Sources */,
35B82BFA1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */,
966FCF4E1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */,
4BC5768125E3C8AB006E06EB /* MGLObserver.mm in Sources */,
8989B17E201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */,
353AFA161D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */,
1FCAE2A420B872A400C577DD /* MGLLocationManager.m in Sources */,
@@ -3062,6 +3105,7 @@
DA8848241CBAFA6200AB86E3 /* MGLOfflineStorage.mm in Sources */,
DA88482A1CBAFA6200AB86E3 /* MGLTilePyramidOfflineRegion.mm in Sources */,
4049C29F1DB6CD6C00B3F799 /* MGLPointCollection.mm in Sources */,
4BC5769525E3D3D6006E06EB /* MGLUserLocationAnnotationViewStyle.m in Sources */,
35136D3F1D42273000C20EFD /* MGLLineStyleLayer.mm in Sources */,
DA704CC41F65A475004B3F28 /* MGLMapAccessibilityElement.mm in Sources */,
DA72620D1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */,
@@ -3070,6 +3114,7 @@
DA8848271CBAFA6200AB86E3 /* MGLPolyline.mm in Sources */,
35CE61841D4165D9004F2359 /* UIColor+MGLAdditions.mm in Sources */,
3EA93369F61CF70AFA50465D /* MGLRendererConfiguration.m in Sources */,
4BC5767F25E3C8AB006E06EB /* MGLEvent.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3151,6 +3196,7 @@
966FCF4F1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */,
DAA4E4231CBB730400178DFB /* MGLPolygon.mm in Sources */,
8989B17F201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */,
4BC5768225E3C8AB006E06EB /* MGLObserver.mm in Sources */,
353AFA171D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */,
35D13AC61D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */,
1FCAE2A520B872A400C577DD /* MGLLocationManager.m in Sources */,
@@ -3158,6 +3204,7 @@
4049C2A01DB6CD6C00B3F799 /* MGLPointCollection.mm in Sources */,
35136D401D42273000C20EFD /* MGLLineStyleLayer.mm in Sources */,
DA704CC51F65A475004B3F28 /* MGLMapAccessibilityElement.mm in Sources */,
4BC5769625E3D3D6006E06EB /* MGLUserLocationAnnotationViewStyle.m in Sources */,
DA72620E1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */,
DAA4E42F1CBB730400178DFB /* MGLCompactCalloutView.m in Sources */,
3510FFFC1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.mm in Sources */,
@@ -3166,6 +3213,7 @@
35CE61851D4165D9004F2359 /* UIColor+MGLAdditions.mm in Sources */,
DAA4E4241CBB730400178DFB /* MGLPolyline.mm in Sources */,
3EA9366247780E4F252652A8 /* MGLRendererConfiguration.m in Sources */,
4BC5768025E3C8AB006E06EB /* MGLEvent.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -1,10 +1,13 @@
#import "MGLFaux3DUserLocationAnnotationView.h"
#import "MGLMapView.h"
#import "MGLMapViewDelegate.h"
#import "MGLUserLocation.h"
#import "MGLUserLocationHeadingIndicator.h"
#import "MGLUserLocationHeadingArrowLayer.h"
#import "MGLUserLocationHeadingBeamLayer.h"
#import "MGLLocationManager_Private.h"
#import "MGLUserLocationAnnotationViewStyle.h"
const CGFloat MGLUserLocationAnnotationDotSize = 22.0;
const CGFloat MGLUserLocationAnnotationHaloSize = 115.0;
@@ -14,9 +17,12 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
const CGFloat MGLUserLocationApproximateZoomThreshold = 7.0;
@implementation MGLFaux3DUserLocationAnnotationView
{
BOOL _puckModeActivated;
BOOL _approximateModeActivated;
CALayer *_puckDot;
CAShapeLayer *_puckArrow;
@@ -27,6 +33,8 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
CALayer *_dotLayer;
CALayer *_haloLayer;
CALayer *_approximateLayer;
CLLocationDirection _oldHeadingAccuracy;
CLLocationAccuracy _oldHorizontalAccuracy;
double _oldZoom;
@@ -49,28 +57,77 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
if (CLLocationCoordinate2DIsValid(self.userLocation.coordinate))
{
(self.mapView.userTrackingMode == MGLUserTrackingModeFollowWithCourse) ? [self drawPuck] : [self drawDot];
[self updatePitch];
if (@available(iOS 14, *)) {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000
if (![self.mapView.locationManager respondsToSelector:@selector(accuracyAuthorization)] ||
self.mapView.locationManager.accuracyAuthorization == CLAccuracyAuthorizationFullAccuracy) {
[self drawPreciseLocationPuck];
} else {
[self drawApproximate];
[self updatePitch];
}
#endif
} else {
[self drawPreciseLocationPuck];
}
}
}
- (void)drawPreciseLocationPuck {
if (_approximateModeActivated) {
[_approximateLayer removeFromSuperlayer];
_approximateLayer = nil;
_approximateModeActivated = NO;
}
(self.mapView.userTrackingMode == MGLUserTrackingModeFollowWithCourse) ? [self drawPuck] : [self drawDot];
[self updatePitch];
_haloLayer.hidden = ! CLLocationCoordinate2DIsValid(self.mapView.userLocation.coordinate) || self.mapView.userLocation.location.horizontalAccuracy > 10;
}
- (void)setTintColor:(UIColor *)tintColor
{
CGColorRef newTintColor = [tintColor CGColor];
UIColor *puckArrowFillColor = tintColor;
UIColor *puckArrowStrokeColor = tintColor;
UIColor *approximateFillColor = tintColor;
UIColor *accuracyFillColor = tintColor;
UIColor *haloFillColor = tintColor;
UIColor *dotFillColor = tintColor;
UIColor *headingFillColor = tintColor;
if ([self.mapView.delegate respondsToSelector:@selector(mapViewStyleForDefaultUserLocationAnnotationView:)]) {
MGLUserLocationAnnotationViewStyle *style = [self.mapView.delegate mapViewStyleForDefaultUserLocationAnnotationView:self.mapView];
puckArrowFillColor = style.puckArrowFillColor ? style.puckArrowFillColor : puckArrowFillColor;
if (@available(iOS 14, *)) {
approximateFillColor = style.approximateHaloFillColor ? style.approximateHaloFillColor : approximateFillColor;
}
haloFillColor = style.haloFillColor ? style.haloFillColor : haloFillColor;
dotFillColor = style.puckFillColor ? style.puckFillColor : dotFillColor;
headingFillColor = style.puckFillColor ? style.puckFillColor : headingFillColor;
}
if (_puckModeActivated)
{
_puckArrow.fillColor = newTintColor;
_puckArrow.strokeColor = newTintColor;
_puckArrow.fillColor = [puckArrowFillColor CGColor];
_puckArrow.strokeColor = [puckArrowStrokeColor CGColor];
}
else if (_approximateModeActivated)
{
_approximateLayer.backgroundColor = [approximateFillColor CGColor];
}
else
{
_accuracyRingLayer.backgroundColor = newTintColor;
_haloLayer.backgroundColor = newTintColor;
_dotLayer.backgroundColor = newTintColor;
[_headingIndicatorLayer updateTintColor:newTintColor];
_accuracyRingLayer.backgroundColor = [accuracyFillColor CGColor];
_haloLayer.backgroundColor = [haloFillColor CGColor];
_dotLayer.backgroundColor = [dotFillColor CGColor];
[_headingIndicatorLayer updateTintColor:[headingFillColor CGColor]];
}
}
@@ -146,14 +203,26 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
[self updateFrameWithSize:MGLUserLocationAnnotationPuckSize];
}
UIColor *arrowColor = self.mapView.tintColor;
UIColor *puckShadowColor = UIColor.blackColor;
CGFloat shadowOpacity = 0.25;
if ([self.mapView.delegate respondsToSelector:@selector(mapViewStyleForDefaultUserLocationAnnotationView:)]) {
MGLUserLocationAnnotationViewStyle *style = [self.mapView.delegate mapViewStyleForDefaultUserLocationAnnotationView:self.mapView];
arrowColor = style.puckArrowFillColor ? style.puckArrowFillColor : arrowColor;
puckShadowColor = style.puckShadowColor ? style.puckShadowColor : puckShadowColor;
shadowOpacity = style.puckShadowOpacity;
}
// background dot (white with black shadow)
//
if ( ! _puckDot)
{
_puckDot = [self circleLayerWithSize:MGLUserLocationAnnotationPuckSize];
_puckDot.backgroundColor = [[UIColor whiteColor] CGColor];
_puckDot.shadowColor = [[UIColor blackColor] CGColor];
_puckDot.shadowOpacity = 0.25;
_puckDot.shadowColor = [puckShadowColor CGColor];
_puckDot.shadowOpacity = shadowOpacity;
_puckDot.shadowPath = [[UIBezierPath bezierPathWithOvalInRect:_puckDot.bounds] CGPath];
if (self.mapView.camera.pitch)
@@ -175,7 +244,7 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
{
_puckArrow = [CAShapeLayer layer];
_puckArrow.path = [[self puckArrow] CGPath];
_puckArrow.fillColor = [self.mapView.tintColor CGColor];
_puckArrow.fillColor = [arrowColor CGColor];
_puckArrow.bounds = CGRectMake(0, 0, round(MGLUserLocationAnnotationArrowSize), round(MGLUserLocationAnnotationArrowSize));
_puckArrow.position = CGPointMake(CGRectGetMidX(super.bounds), CGRectGetMidY(super.bounds));
_puckArrow.shouldRasterize = YES;
@@ -228,6 +297,20 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
[self updateFrameWithSize:MGLUserLocationAnnotationDotSize];
}
UIColor *haloColor = self.mapView.tintColor;
UIColor *puckBackgroundColor = self.mapView.tintColor;
UIColor *puckShadowColor = UIColor.blackColor;
CGFloat shadowOpacity = 0.25;
if ([self.mapView.delegate respondsToSelector:@selector(mapViewStyleForDefaultUserLocationAnnotationView:)]) {
MGLUserLocationAnnotationViewStyle *style = [self.mapView.delegate mapViewStyleForDefaultUserLocationAnnotationView:self.mapView];
haloColor = style.haloFillColor ? style.haloFillColor : haloColor;
puckBackgroundColor = style.puckFillColor ? style.puckFillColor : puckBackgroundColor;
puckShadowColor = style.puckShadowColor ? style.puckShadowColor : puckShadowColor;
shadowOpacity = style.puckShadowOpacity;
}
// heading indicator (tinted, beam or arrow)
//
BOOL headingTrackingModeEnabled = self.mapView.userTrackingMode == MGLUserTrackingModeFollowWithHeading;
@@ -251,11 +334,13 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
if (headingTrackingModeEnabled)
{
_headingIndicatorLayer = [[MGLUserLocationHeadingBeamLayer alloc] initWithUserLocationAnnotationView:self];
[_headingIndicatorLayer updateTintColor:[haloColor CGColor]];
[self.layer insertSublayer:_headingIndicatorLayer below:_dotBorderLayer];
}
else
{
_headingIndicatorLayer = [[MGLUserLocationHeadingArrowLayer alloc] initWithUserLocationAnnotationView:self];
[_headingIndicatorLayer updateTintColor:[puckBackgroundColor CGColor]];
[self.layer addSublayer:_headingIndicatorLayer];
_headingIndicatorLayer.zPosition = 1;
}
@@ -351,7 +436,7 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
if ( ! _haloLayer)
{
_haloLayer = [self circleLayerWithSize:MGLUserLocationAnnotationHaloSize];
_haloLayer.backgroundColor = [self.mapView.tintColor CGColor];
_haloLayer.backgroundColor = [haloColor CGColor];
_haloLayer.allowsGroupOpacity = NO;
_haloLayer.zPosition = -0.1f;
@@ -381,8 +466,8 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
{
_dotBorderLayer = [self circleLayerWithSize:MGLUserLocationAnnotationDotSize];
_dotBorderLayer.backgroundColor = [[UIColor whiteColor] CGColor];
_dotBorderLayer.shadowColor = [[UIColor blackColor] CGColor];
_dotBorderLayer.shadowOpacity = 0.25;
_dotBorderLayer.shadowColor = [puckShadowColor CGColor];
_dotBorderLayer.shadowOpacity = shadowOpacity;
_dotBorderLayer.shadowPath = [[UIBezierPath bezierPathWithOvalInRect:_dotBorderLayer.bounds] CGPath];
if (self.mapView.camera.pitch)
@@ -403,7 +488,7 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
if ( ! _dotLayer)
{
_dotLayer = [self circleLayerWithSize:MGLUserLocationAnnotationDotSize * 0.75];
_dotLayer.backgroundColor = [self.mapView.tintColor CGColor];
_dotLayer.backgroundColor = [puckBackgroundColor CGColor];
// set defaults for the animations
CAAnimationGroup *animationGroup = [self loopingAnimationGroupWithDuration:1.5];
@@ -435,6 +520,85 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
}
}
- (void)drawApproximate
{
if ( ! _approximateModeActivated)
{
self.layer.sublayers = nil;
_headingIndicatorLayer = nil;
_dotBorderLayer = nil;
_dotLayer = nil;
_accuracyRingLayer = nil;
_haloLayer = nil;
_puckDot = nil;
_puckArrow = nil;
_approximateModeActivated = YES;
}
UIColor *backgroundColor = self.mapView.tintColor;
UIColor *strokeColor = UIColor.blackColor;
CGFloat borderSize = 2.0;
CGFloat opacity = 0.25;
if ([self.mapView.delegate respondsToSelector:@selector(mapViewStyleForDefaultUserLocationAnnotationView:)]) {
MGLUserLocationAnnotationViewStyle *style = [self.mapView.delegate mapViewStyleForDefaultUserLocationAnnotationView:self.mapView];
if (@available(iOS 14, *)) {
backgroundColor = style.approximateHaloFillColor ? style.approximateHaloFillColor : backgroundColor;
strokeColor = style.approximateHaloBorderColor ? style.approximateHaloBorderColor : strokeColor;
opacity = style.approximateHaloOpacity;
borderSize = style.approximateHaloBorderWidth;
}
}
// update approximate ring (if zoom or horizontal accuracy have changed)
if (_approximateLayer && (_oldZoom != self.mapView.zoomLevel || _oldHorizontalAccuracy != self.userLocation.location.horizontalAccuracy))
{
CGFloat borderWidth = 2;
if (self.mapView.zoomLevel < MGLUserLocationApproximateZoomThreshold) {
borderSize = 3;
}
_approximateLayer.borderWidth = borderSize;
if (self.mapView.zoomLevel >= MGLUserLocationApproximateZoomThreshold) {
CGFloat accuracyRingSize = [self calculateAccuracyRingSize];
_approximateLayer.hidden = NO;
// disable implicit animation of the accuracy ring, unless triggered by a change in accuracy
BOOL shouldDisableActions = _oldHorizontalAccuracy == self.userLocation.location.horizontalAccuracy;
[CATransaction begin];
[CATransaction setDisableActions:shouldDisableActions];
_approximateLayer.bounds = CGRectMake(0, 0, accuracyRingSize, accuracyRingSize);
_approximateLayer.cornerRadius = accuracyRingSize / 2.0;
[CATransaction commit];
}
// store accuracy and zoom so we're not redrawing unchanged location updates
_oldHorizontalAccuracy = self.userLocation.location.horizontalAccuracy;
_oldZoom = self.mapView.zoomLevel;
}
// approximate ring
if ( ! _approximateLayer && self.userLocation.location.horizontalAccuracy)
{
CGFloat accuracyRingSize = [self calculateAccuracyRingSize];
_approximateLayer = [self circleLayerWithSize:accuracyRingSize];
approximateLayer.backgroundColor = [backgroundColor CGColor];
_approximateLayer.opacity = opacity;
_approximateLayer.shouldRasterize = NO;
_approximateLayer.allowsGroupOpacity = NO;
_approximateLayer.borderWidth = borderSize;
_approximateLayer.borderColor = [strokeColor CGColor];
[self.layer addSublayer:_approximateLayer];
}
}
- (CALayer *)circleLayerWithSize:(CGFloat)layerSize
{
layerSize = round(layerSize);

View File

@@ -6,6 +6,7 @@
#import "MGLMapCamera.h"
#import "MGLTypes.h"
#import "MGLStyle.h"
#import "MGLObserver.h"
NS_ASSUME_NONNULL_BEGIN
@@ -188,7 +189,7 @@ FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLUserLocationAnnotationTyp
Simple map view</a> example to learn how to initialize a basic `MGLMapView`.
*/
MGL_EXPORT
@interface MGLMapView : UIView <MGLStylable>
@interface MGLMapView : UIView <MGLStylable, MGLObservable>
#pragma mark Creating Instances
@@ -2002,6 +2003,14 @@ MGL_EXPORT
*/
@property (nonatomic) MGLMapDebugMaskOptions debugMask;
/**
:nodoc:
Convenience method for subscribing to a single event. See `-[MGLObservable
subscribeForObserver:events:]`.
*/
- (void)subscribeForObserver:(nonnull MGLObserver *)observer
event:(nonnull MGLEventType)event;
@end
NS_ASSUME_NONNULL_END

View File

@@ -67,6 +67,8 @@
#import "MGLLoggingConfiguration_Private.h"
#import "MGLNetworkConfiguration_Private.h"
#import "MGLReachability.h"
#import "MGLObserver_Private.h"
#import "MGLEvent_Private.h"
#include <algorithm>
#include <cstdlib>
@@ -277,6 +279,8 @@ public:
@property (nonatomic, assign) UIEdgeInsets safeMapViewContentInsets;
@property (nonatomic, strong) NSNumber *automaticallyAdjustContentInsetHolder;
@property (nonatomic) NSMutableSet<MGLObserver *> *observerCache;
@end
@implementation MGLMapView
@@ -517,6 +521,8 @@ public:
_annotationViewReuseQueueByIdentifier = [NSMutableDictionary dictionary];
_selectedAnnotationTag = MGLAnnotationTagNotFound;
_annotationsNearbyLastTap = {};
_observerCache = [NSMutableSet set];
// TODO: This warning should be removed when automaticallyAdjustsScrollViewInsets is removed from
// the UIViewController api.
@@ -5417,6 +5423,11 @@ public:
}
}
- (NSString *)accuracyDescriptionString {
NSDictionary *dictionary = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationTemporaryUsageDescriptionDictionary"];
return dictionary[@"MGLAccuracyAuthorizationDescription"];
}
- (void)setShowsUserLocation:(BOOL)showsUserLocation
{
MGLLogDebug(@"Setting showsUserLocation: %@", MGLStringFromBOOL(showsUserLocation));
@@ -5975,6 +5986,47 @@ public:
}
}
- (void)locationManagerDidChangeAuthorization:(id<MGLLocationManager>)manager
{
if (![self shouldShowLocationDotBasedOnCurrentLocationPermissions])
{
[self.userLocationAnnotationView removeFromSuperview];
[self.locationManager stopUpdatingLocation];
[self.locationManager stopUpdatingHeading];
} else {
if (@available(iOS 14, *)) {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000
if (self.userTrackingMode != MGLUserTrackingModeNone &&
[manager respondsToSelector:@selector(authorizationStatus)] &&
(manager.authorizationStatus != kCLAuthorizationStatusRestricted ||
manager.authorizationStatus != kCLAuthorizationStatusAuthorizedAlways ||
manager.authorizationStatus != kCLAuthorizationStatusAuthorizedWhenInUse) &&
[manager respondsToSelector:@selector(accuracyAuthorization)] &&
manager.accuracyAuthorization == CLAccuracyAuthorizationReducedAccuracy &&
[self accuracyDescriptionString] != nil ) {
[self.locationManager requestTemporaryFullAccuracyAuthorizationWithPurposeKey:@"MGLAccuracyAuthorizationDescription"];
} else {
[self validateLocationServices];
}
#endif
} else {
[self validateLocationServices];
}
}
if (@available(iOS 14, *)) {
if ([self.delegate respondsToSelector:@selector(mapView:didChangeLocationManagerAuthorization:)]) {
[self.delegate mapView:self didChangeLocationManagerAuthorization:manager];
}
}
}
- (BOOL)shouldShowLocationDotBasedOnCurrentLocationPermissions
{
return self.locationManager && (self.locationManager.authorizationStatus == kCLAuthorizationStatusAuthorizedAlways
|| self.locationManager.authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse);
}
- (void)updateHeadingForDeviceOrientation
{
if (self.locationManager)
@@ -6840,6 +6892,41 @@ public:
return _annotationViewReuseQueueByIdentifier[identifier];
}
#pragma mark - MGLObservable methods -
static std::vector<std::string> vectorOfStringsFromSet(NSSet<NSString *> *setOfStrings) {
__block std::vector<std::string> strings;
strings.reserve(setOfStrings.count);
[setOfStrings enumerateObjectsUsingBlock:^(NSString * _Nonnull text, BOOL * _Nonnull stop) {
strings.push_back(text.UTF8String);
}];
return strings;
}
// Convenience method
- (void)subscribeForObserver:(nonnull MGLObserver *)observer event:(nonnull MGLEventType)event {
[self subscribeForObserver:observer events:[NSSet setWithObject:event]];
}
- (void)subscribeForObserver:(MGLObserver *)observer events:(nonnull NSSet<MGLEventType> *)events {
observer.observing = YES;
[self.observerCache addObject:observer];
auto eventTypes = vectorOfStringsFromSet(events);
self.mbglMap.subscribe(observer.peer, eventTypes);
}
- (void)unsubscribeForObserver:(MGLObserver *)observer events:(nonnull NSSet<MGLEventType> *)events {
auto eventTypes = vectorOfStringsFromSet(events);
self.mbglMap.unsubscribe(observer.peer, eventTypes);
}
- (void)unsubscribeForObserver:(MGLObserver *)observer {
self.mbglMap.unsubscribe(observer.peer);
[self.observerCache removeObject:observer];
observer.observing = NO;
}
@end
#pragma mark - IBAdditions methods

View File

@@ -6,6 +6,7 @@
NS_ASSUME_NONNULL_BEGIN
@class MGLMapView;
@class MGLUserLocationAnnotationViewStyle;
/**
The `MGLMapViewDelegate` protocol defines a set of optional methods that you
@@ -313,6 +314,16 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)mapViewDidStopLocatingUser:(MGLMapView *)mapView;
/**
Asks the delegate styling options for each default user location annotation view.
This method is called many times during gesturing, so you should avoid performing
complex or performance-intensive tasks in your implementation.
@param mapView The map view that is tracking the user’s location.
*/
- (MGLUserLocationAnnotationViewStyle *)mapViewStyleForDefaultUserLocationAnnotationView:(MGLMapView *)mapView NS_SWIFT_NAME(mapView(styleForDefaultUserLocationAnnotationView:));
/**
Tells the delegate that the location of the user was updated.
@@ -368,6 +379,18 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (CGPoint)mapViewUserLocationAnchorPoint:(MGLMapView *)mapView;
/**
Tells the delegate that the map's location updates accuracy authorization has changed.
This method is called after the user changes location accuracy authorization when
requesting location permissions or in privacy settings.
@param mapView The map view that changed its location accuracy authorization.
@param manager The location manager reporting the update.
*/
- (void)mapView:(MGLMapView *)mapView didChangeLocationManagerAuthorization:(id<MGLLocationManager>)manager API_AVAILABLE(ios(14));
#pragma mark Managing the Appearance of Annotations
/**

View File

@@ -0,0 +1,57 @@
#import <UIKit/UIKit.h>
#import "MGLFoundation.h"
NS_ASSUME_NONNULL_BEGIN
/**
A class containing information about the default User Location annotation view style.
*/
MGL_EXPORT
@interface MGLUserLocationAnnotationViewStyle : NSObject
/**
The fill color for the puck view.
*/
@property (nonatomic) UIColor *puckFillColor;
/**
The shadow color for the puck view.
*/
@property (nonatomic) UIColor *puckShadowColor;
/**
The shadow opacity for the puck view.
Set any value between 0.0 and 1.0.
The default value of this property is equal to `0.25`
*/
@property (nonatomic, assign) CGFloat puckShadowOpacity;
/**
The fill color for the arrow puck.
*/
@property (nonatomic) UIColor *puckArrowFillColor;
/**
The fill color for the puck view.
*/
@property (nonatomic) UIColor *haloFillColor;
/**
The halo fill color for the approximate view.
*/
@property (nonatomic) UIColor *approximateHaloFillColor API_AVAILABLE(ios(14));
/**
The halo border color for the approximate view.
*/
@property (nonatomic) UIColor *approximateHaloBorderColor API_AVAILABLE(ios(14));
/**
The halo border width for the approximate view.
The default value of this property is equal to `2.0`
*/
@property (nonatomic, assign) CGFloat approximateHaloBorderWidth API_AVAILABLE(ios(14));
/**
The halo opacity for the approximate view.
Set any value between 0.0 and 1.0
The default value of this property is equal to `0.15`
*/
@property (nonatomic, assign) CGFloat approximateHaloOpacity API_AVAILABLE(ios(14));
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,62 @@
#import "MGLUserLocationAnnotationViewStyle.h"
#import "MGLLoggingConfiguration_Private.h"
@implementation MGLUserLocationAnnotationViewStyle
- (instancetype)init {
if ((self = [super init])) {
self.puckShadowOpacity = 0.25;
if (@available(iOS 14, *)) {
self.approximateHaloBorderWidth = 2.0;
self.approximateHaloOpacity = 0.15;
}
}
return self;
}
- (void)setPuckFillColor:(UIColor *)puckFillColor {
MGLLogDebug(@"Setting puckFillColor: %@", puckFillColor);
_puckFillColor = puckFillColor;
}
- (void)setPuckShadowColor:(UIColor *)puckShadowColor {
MGLLogDebug(@"Setting puckShadowColor: %@", puckShadowColor);
_puckShadowColor = puckShadowColor;
}
- (void)setPuckShadowOpacity:(CGFloat)puckShadowOpacity {
MGLLogDebug(@"Setting puckShadowOpacity: %.2f", puckShadowOpacity);
_puckShadowOpacity = puckShadowOpacity;
}
- (void)setPuckArrowFillColor:(UIColor *)puckArrowFillColor {
MGLLogDebug(@"Setting puckArrowFillColor: %@", puckArrowFillColor);
_puckArrowFillColor = puckArrowFillColor;
}
- (void)setHaloFillColor:(UIColor *)haloFillColor {
MGLLogDebug(@"Setting haloFillColor: %@", haloFillColor);
_haloFillColor = haloFillColor;
}
- (void)setApproximateHaloFillColor:(UIColor *)approximateHaloFillColor {
MGLLogDebug(@"Setting approximateHaloFillColor: %@", approximateHaloFillColor);
_approximateHaloFillColor = approximateHaloFillColor;
}
- (void)setApproximateHaloBorderColor:(UIColor *)approximateHaloBorderColor {
MGLLogDebug(@"Setting approximateHaloBorderColor: %@", approximateHaloBorderColor);
_approximateHaloBorderColor = approximateHaloBorderColor;
}
- (void)setApproximateHaloBorderWidth:(CGFloat)approximateHaloBorderWidth {
MGLLogDebug(@"Setting approximateHaloBorderWidth: %.2f", approximateHaloBorderWidth);
_approximateHaloBorderWidth = approximateHaloBorderWidth;
}
- (void)setApproximateHaloOpacity:(CGFloat)approximateHaloOpacity {
MGLLogDebug(@"Setting approximateHaloOpacity: %.2f", approximateHaloOpacity);
_approximateHaloOpacity = approximateHaloOpacity;
}
@end

View File

@@ -1,5 +1,4 @@
#import <Foundation/Foundation.h>
#import "MGLFoundation.h"
/// Project version number for Mapbox.
@@ -8,69 +7,73 @@ FOUNDATION_EXPORT MGL_EXPORT double MapboxVersionNumber;
/// Project version string for Mapbox.
FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "MGLAnnotationView.h"
#import "MGLAccountManager.h"
#import "MGLAnnotation.h"
#import "MGLAnnotationImage.h"
#import "MGLAnnotationView.h"
#import "MGLAttributedExpression.h"
#import "MGLAttributionInfo.h"
#import "MGLBackgroundStyleLayer.h"
#import "MGLCalloutView.h"
#import "MGLCircleStyleLayer.h"
#import "MGLClockDirectionFormatter.h"
#import "MGLCluster.h"
#import "MGLCompassButton.h"
#import "MGLCompassDirectionFormatter.h"
#import "MGLComputedShapeSource.h"
#import "MGLCoordinateFormatter.h"
#import "MGLDistanceFormatter.h"
#import "MGLEvent.h"
#import "MGLFeature.h"
#import "MGLFillExtrusionStyleLayer.h"
#import "MGLFillStyleLayer.h"
#import "MGLForegroundStyleLayer.h"
#import "MGLGeometry.h"
#import "MGLHeatmapStyleLayer.h"
#import "MGLHillshadeStyleLayer.h"
#import "MGLImageSource.h"
#import "MGLLight.h"
#import "MGLLineStyleLayer.h"
#import "MGLLocationManager.h"
#import "MGLLoggingConfiguration.h"
#import "MGLMapCamera.h"
#import "MGLMapSnapshotter.h"
#import "MGLMapView.h"
#import "MGLMapView+IBAdditions.h"
#import "MGLMapViewDelegate.h"
#import "MGLMapView+IBAdditions.h"
#import "MGLMultiPoint.h"
#import "MGLNetworkConfiguration.h"
#import "MGLObserver.h"
#import "MGLOfflinePack.h"
#import "MGLOfflineRegion.h"
#import "MGLOfflineStorage.h"
#import "MGLOpenGLStyleLayer.h"
#import "MGLOverlay.h"
#import "MGLPointAnnotation.h"
#import "MGLPointCollection.h"
#import "MGLPolygon.h"
#import "MGLPolyline.h"
#import "MGLRasterDEMSource.h"
#import "MGLRasterStyleLayer.h"
#import "MGLRasterTileSource.h"
#import "MGLSDKMetricsManager.h"
#import "MGLShape.h"
#import "MGLShapeCollection.h"
#import "MGLShapeOfflineRegion.h"
#import "MGLShapeSource.h"
#import "MGLSource.h"
#import "MGLStyle.h"
#import "MGLStyleLayer.h"
#import "MGLForegroundStyleLayer.h"
#import "MGLVectorStyleLayer.h"
#import "MGLFillExtrusionStyleLayer.h"
#import "MGLFillStyleLayer.h"
#import "MGLLineStyleLayer.h"
#import "MGLStyleValue.h"
#import "MGLSymbolStyleLayer.h"
#import "MGLRasterStyleLayer.h"
#import "MGLCircleStyleLayer.h"
#import "MGLHeatmapStyleLayer.h"
#import "MGLHillshadeStyleLayer.h"
#import "MGLBackgroundStyleLayer.h"
#import "MGLOpenGLStyleLayer.h"
#import "MGLSource.h"
#import "MGLTileSource.h"
#import "MGLVectorTileSource.h"
#import "MGLShapeSource.h"
#import "MGLComputedShapeSource.h"
#import "MGLRasterTileSource.h"
#import "MGLRasterDEMSource.h"
#import "MGLImageSource.h"
#import "MGLShapeOfflineRegion.h"
#import "MGLTilePyramidOfflineRegion.h"
#import "MGLTileSource.h"
#import "MGLTypes.h"
#import "MGLUserLocation.h"
#import "MGLUserLocationAnnotationView.h"
#import "NSValue+MGLAdditions.h"
#import "MGLStyleValue.h"
#import "MGLAttributionInfo.h"
#import "MGLMapSnapshotter.h"
#import "MGLVectorStyleLayer.h"
#import "MGLVectorTileSource.h"
#import "NSExpression+MGLAdditions.h"
#import "NSPredicate+MGLAdditions.h"
#import "MGLLocationManager.h"
#import "MGLLoggingConfiguration.h"
#import "MGLNetworkConfiguration.h"
#import "MGLAttributedExpression.h"
#import "NSValue+MGLAdditions.h"
#import "MGLUserLocationAnnotationViewStyle.h"

View File

@@ -100,4 +100,8 @@ extension MGLMapViewDelegateIntegrationTests: MGLMapViewDelegate {
func mapView(_ mapView: MGLMapView, didFailToLoadImage imageName: String) -> UIImage? { return nil }
func mapView(_ mapView: MGLMapView, shouldRemoveStyleImage imageName: String) -> Bool { return false }
func mapView(_ mapView: MGLMapView, didChangeLocationManagerAuthorization manager: MGLLocationManager) {}
func mapView(styleForDefaultUserLocationAnnotationView mapView: MGLMapView) -> MGLUserLocationAnnotationViewStyle { return MGLUserLocationAnnotationViewStyle() }
}