summaryrefslogtreecommitdiff
path: root/platform/ios/src
diff options
context:
space:
mode:
Diffstat (limited to 'platform/ios/src')
-rw-r--r--platform/ios/src/MGLAPIClient.h1
-rw-r--r--platform/ios/src/MGLAPIClient.m11
-rw-r--r--platform/ios/src/MGLAnnotationImage.h2
-rw-r--r--platform/ios/src/MGLAnnotationImage.m35
-rw-r--r--platform/ios/src/MGLAnnotationView.h2
-rw-r--r--platform/ios/src/MGLAnnotationView.mm28
-rw-r--r--platform/ios/src/MGLCalloutView.h18
-rw-r--r--platform/ios/src/MGLCompactCalloutView.m8
-rw-r--r--platform/ios/src/MGLMapView.h9
-rw-r--r--platform/ios/src/MGLMapView.mm89
-rw-r--r--platform/ios/src/MGLMapViewDelegate.h17
-rw-r--r--platform/ios/src/MGLMapView_Private.h6
-rw-r--r--platform/ios/src/MGLMapboxEvents.m71
-rw-r--r--platform/ios/src/MGLUserLocation.h2
-rw-r--r--platform/ios/src/MGLUserLocation.m38
-rw-r--r--platform/ios/src/Mapbox.h1
16 files changed, 236 insertions, 102 deletions
diff --git a/platform/ios/src/MGLAPIClient.h b/platform/ios/src/MGLAPIClient.h
index 0f8926d360..4e5ea3b5e0 100644
--- a/platform/ios/src/MGLAPIClient.h
+++ b/platform/ios/src/MGLAPIClient.h
@@ -9,7 +9,6 @@ NS_ASSUME_NONNULL_BEGIN
- (void)postEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler;
- (void)postEvent:(MGLMapboxEventAttributes *)event completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler;
-- (void)cancelAll;
@end
diff --git a/platform/ios/src/MGLAPIClient.m b/platform/ios/src/MGLAPIClient.m
index 7fb6538e5d..5e8ee5fe1d 100644
--- a/platform/ios/src/MGLAPIClient.m
+++ b/platform/ios/src/MGLAPIClient.m
@@ -21,7 +21,6 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST";
@property (nonatomic, copy) NSData *geoTrustCert;
@property (nonatomic, copy) NSData *testServerCert;
@property (nonatomic, copy) NSString *userAgent;
-@property (nonatomic) NSMutableArray *dataTasks;
@property (nonatomic) BOOL usesTestServer;
@end
@@ -33,7 +32,6 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST";
if (self) {
_session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
delegate:self delegateQueue:nil];
- _dataTasks = [NSMutableArray array];
[self loadCertificates];
[self setupBaseURL];
[self setupUserAgent];
@@ -59,24 +57,15 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST";
error = error ?: statusError;
completionHandler(error);
}
- [self.dataTasks removeObject:dataTask];
dataTask = nil;
}];
[dataTask resume];
- if (dataTask) {
- [self.dataTasks addObject:dataTask];
- }
}
- (void)postEvent:(nonnull MGLMapboxEventAttributes *)event completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler {
[self postEvents:@[event] completionHandler:completionHandler];
}
-- (void)cancelAll {
- [self.dataTasks makeObjectsPerformSelector:@selector(cancel)];
- [self.dataTasks removeAllObjects];
-}
-
#pragma mark Utilities
- (NSURLRequest *)requestForEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events {
diff --git a/platform/ios/src/MGLAnnotationImage.h b/platform/ios/src/MGLAnnotationImage.h
index a7003d7f91..95bce21f51 100644
--- a/platform/ios/src/MGLAnnotationImage.h
+++ b/platform/ios/src/MGLAnnotationImage.h
@@ -8,7 +8,7 @@ NS_ASSUME_NONNULL_BEGIN
objects and may be recycled later and put into a reuse queue that is maintained
by the map view.
*/
-@interface MGLAnnotationImage : NSObject
+@interface MGLAnnotationImage : NSObject <NSSecureCoding>
#pragma mark Initializing and Preparing the Image Object
diff --git a/platform/ios/src/MGLAnnotationImage.m b/platform/ios/src/MGLAnnotationImage.m
index e1085be98d..9c9c175ab9 100644
--- a/platform/ios/src/MGLAnnotationImage.m
+++ b/platform/ios/src/MGLAnnotationImage.m
@@ -30,6 +30,41 @@
return self;
}
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ if (self = [super init]) {
+ _image = [decoder decodeObjectOfClass:[UIImage class] forKey:@"image"];
+ _reuseIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"reuseIdentifier"];
+ _enabled = [decoder decodeBoolForKey:@"enabled"];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [coder encodeObject:_image forKey:@"image"];
+ [coder encodeObject:_reuseIdentifier forKey:@"reuseIdentifier"];
+ [coder encodeBool:_enabled forKey:@"enabled"];
+}
+
+- (BOOL)isEqual:(id)other {
+ if (self == other) return YES;
+ if (![other isKindOfClass:[MGLAnnotationImage class]]) return NO;
+
+ MGLAnnotationImage *otherAnnotationImage = other;
+
+ return ((!_reuseIdentifier && !otherAnnotationImage.reuseIdentifier)
+ || [_reuseIdentifier isEqualToString:otherAnnotationImage.reuseIdentifier])
+ && _enabled == otherAnnotationImage.enabled
+ && (_image == otherAnnotationImage.image || [UIImagePNGRepresentation(_image) isEqualToData:UIImagePNGRepresentation(otherAnnotationImage.image)]);
+}
+
+- (NSUInteger)hash {
+ return _reuseIdentifier.hash + _enabled + _image.hash;
+}
+
- (void)setImage:(UIImage *)image {
_image = image;
[self.delegate annotationImageNeedsRedisplay:self];
diff --git a/platform/ios/src/MGLAnnotationView.h b/platform/ios/src/MGLAnnotationView.h
index 634e9ad723..d159976a4c 100644
--- a/platform/ios/src/MGLAnnotationView.h
+++ b/platform/ios/src/MGLAnnotationView.h
@@ -50,7 +50,7 @@ typedef NS_ENUM(NSUInteger, MGLAnnotationViewDragState) {
interactivity such as dragging, you can use an `MGLAnnotationImage` instead to
conserve memory and optimize drawing performance.
*/
-@interface MGLAnnotationView : UIView
+@interface MGLAnnotationView : UIView <NSSecureCoding>
#pragma mark Initializing and Preparing the View
diff --git a/platform/ios/src/MGLAnnotationView.mm b/platform/ios/src/MGLAnnotationView.mm
index 96ed8c733e..d2243bdf23 100644
--- a/platform/ios/src/MGLAnnotationView.mm
+++ b/platform/ios/src/MGLAnnotationView.mm
@@ -33,6 +33,34 @@
return self;
}
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ if (self = [super initWithCoder:decoder]) {
+ _reuseIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"reuseIdentifier"];
+ _annotation = [decoder decodeObjectOfClass:[NSObject class] forKey:@"annotation"];
+ _centerOffset = [decoder decodeCGVectorForKey:@"centerOffset"];
+ _scalesWithViewingDistance = [decoder decodeBoolForKey:@"scalesWithViewingDistance"];
+ _selected = [decoder decodeBoolForKey:@"selected"];
+ _enabled = [decoder decodeBoolForKey:@"enabled"];
+ self.draggable = [decoder decodeBoolForKey:@"draggable"];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+ [coder encodeObject:_reuseIdentifier forKey:@"reuseIdentifier"];
+ [coder encodeObject:_annotation forKey:@"annotation"];
+ [coder encodeCGVector:_centerOffset forKey:@"centerOffset"];
+ [coder encodeBool:_scalesWithViewingDistance forKey:@"scalesWithViewingDistance"];
+ [coder encodeBool:_selected forKey:@"selected"];
+ [coder encodeBool:_enabled forKey:@"enabled"];
+ [coder encodeBool:_draggable forKey:@"draggable"];
+}
+
- (void)prepareForReuse
{
// Intentionally left blank. The default implementation of this method does nothing.
diff --git a/platform/ios/src/MGLCalloutView.h b/platform/ios/src/MGLCalloutView.h
index 6918aad614..4dc9a25be4 100644
--- a/platform/ios/src/MGLCalloutView.h
+++ b/platform/ios/src/MGLCalloutView.h
@@ -46,6 +46,24 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)dismissCalloutAnimated:(BOOL)animated;
+@optional
+
+/**
+ A Boolean value indicating whether the callout view should be anchored to
+ the corresponding annotation. You can adjust the callout view’s precise location by
+ overriding -[UIView setCenter:]. The callout view will not be anchored to the
+ annotation if this optional property is unimplemented.
+ */
+@property (nonatomic, readonly, assign, getter=isAnchoredToAnnotation) BOOL anchoredToAnnotation;
+
+/**
+ A Boolean value indicating whether the callout view should be dismissed automatically
+ when the map view’s viewport changes. Note that a single tap on the map view
+ still dismisses the callout view regardless of the value of this property.
+ The callout view will be dismissed if this optional property is unimplemented.
+ */
+@property (nonatomic, readonly, assign) BOOL dismissesAutomatically;
+
@end
/**
diff --git a/platform/ios/src/MGLCompactCalloutView.m b/platform/ios/src/MGLCompactCalloutView.m
index 49812c51a4..3d2118ca38 100644
--- a/platform/ios/src/MGLCompactCalloutView.m
+++ b/platform/ios/src/MGLCompactCalloutView.m
@@ -14,6 +14,14 @@
return [[self alloc] init];
}
+- (BOOL)isAnchoredToAnnotation {
+ return YES;
+}
+
+- (BOOL)dismissesAutomatically {
+ return NO;
+}
+
- (void)setRepresentedObject:(id <MGLAnnotation>)representedObject
{
_representedObject = representedObject;
diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h
index 771a48c7ff..62f053e96b 100644
--- a/platform/ios/src/MGLMapView.h
+++ b/platform/ios/src/MGLMapView.h
@@ -123,6 +123,13 @@ IB_DESIGNABLE
Unlike the `styleURL` property, this property is set to an object that allows
you to manipulate every aspect of the style locally.
+ If the style is loading, 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
+ `-[MGLMapViewDelegate mapView:didFinishLoadingStyle:]` or
+ `-[MGLMapViewDelegate mapViewDidFinishLoadingMap:]` 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
@@ -130,7 +137,7 @@ IB_DESIGNABLE
`+[MGLStyle outdoorsStyleURLWithVersion:]`, `MGLMapView`'s “Style URL”
inspectable in Interface Builder, or a manually constructed `NSURL`.
*/
-@property (nonatomic, readonly) MGLStyle *style;
+@property (nonatomic, readonly, nullable) MGLStyle *style;
/**
URLs of the styles bundled with the library.
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 608acf016d..c211d4419b 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -7,6 +7,7 @@
#import <GLKit/GLKit.h>
#import <OpenGLES/EAGL.h>
+#include <mbgl/map/map.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/sprite/sprite_image.hpp>
@@ -347,6 +348,11 @@ public:
return self;
}
++ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyle
+{
+ return [NSSet setWithObject:@"styleURL"];
+}
+
+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyleURL
{
return [NSSet setWithObjects:@"styleURL__", nil];
@@ -369,10 +375,8 @@ public:
}
styleURL = styleURL.mgl_URLByStandardizingScheme;
- [self willChangeValueForKey:@"style"];
- _style = [[MGLStyle alloc] initWithMapView:self];
+ self.style = nil;
_mbglMap->setStyleURL([[styleURL absoluteString] UTF8String]);
- [self didChangeValueForKey:@"style"];
}
- (IBAction)reloadStyle:(__unused id)sender {
@@ -1734,7 +1738,7 @@ public:
_attributionInfos = [self.style attributionInfosWithFontSize:[UIFont buttonFontSize] linkColor:nil];
for (MGLAttributionInfo *info in _attributionInfos)
{
- NSString *title = [info.title.string capitalizedStringWithLocale:[NSLocale currentLocale]];
+ NSString *title = [info.title.string mgl_titleCasedStringWithLocale:[NSLocale currentLocale]];
[self.attributionSheet addButtonWithTitle:title];
}
@@ -3137,23 +3141,9 @@ public:
- (double)alphaForShapeAnnotation:(MGLShape *)annotation
{
- // The explicit -mapView:alphaForShapeAnnotation: delegate method is deprecated
- // but still used, if implemented. When not implemented, call the stroke or
- // fill color delegate methods and pull the alpha from the returned color.
if (_delegateHasAlphasForShapeAnnotations)
{
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [self.delegate mapView:self alphaForShapeAnnotation:annotation];
-#pragma clang diagnostic pop
- }
- else if ([annotation isKindOfClass:[MGLPolygon class]])
- {
- return [self fillColorForPolygonAnnotation:(MGLPolygon *)annotation].a ?: 1.0;
- }
- else if ([annotation isKindOfClass:[MGLShape class]])
- {
- return [self strokeColorForShapeAnnotation:annotation].a ?: 1.0;
}
return 1.0;
}
@@ -4566,21 +4556,34 @@ public:
|| self.userTrackingMode == MGLUserTrackingModeNone
|| self.userTrackingState != MGLUserTrackingStateChanged)
{
- // Deselect annotation if it lies outside the viewport
- if (self.selectedAnnotation) {
- MGLAnnotationTag tag = [self annotationTagForAnnotation:self.selectedAnnotation];
- MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(tag);
- MGLAnnotationView *annotationView = annotationContext.annotationView;
-
- CGRect rect = [self positioningRectForCalloutForAnnotationWithTag:tag];
-
- if (annotationView)
- {
- rect = annotationView.frame;
- }
-
- if ( ! CGRectIntersectsRect(rect, self.frame)) {
- [self deselectAnnotation:self.selectedAnnotation animated:NO];
+ UIView<MGLCalloutView> *calloutView = self.calloutViewForSelectedAnnotation;
+ BOOL dismissesAutomatically = (calloutView
+ && [calloutView respondsToSelector:@selector(dismissesAutomatically)]
+ && calloutView.dismissesAutomatically);
+ // dismissesAutomatically is an optional property and we want to dismiss
+ // the callout view if it's unimplemented.
+ if (dismissesAutomatically || ![calloutView respondsToSelector:@selector(dismissesAutomatically)])
+ {
+ [self deselectAnnotation:self.selectedAnnotation animated:NO];
+ }
+ else
+ {
+ // Deselect annotation if it lies outside the viewport
+ if (self.selectedAnnotation) {
+ MGLAnnotationTag tag = [self annotationTagForAnnotation:self.selectedAnnotation];
+ MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(tag);
+ MGLAnnotationView *annotationView = annotationContext.annotationView;
+
+ CGRect rect = [self positioningRectForCalloutForAnnotationWithTag:tag];
+
+ if (annotationView)
+ {
+ rect = annotationView.frame;
+ }
+
+ if ( ! CGRectIntersectsRect(rect, self.frame)) {
+ [self deselectAnnotation:self.selectedAnnotation animated:NO];
+ }
}
}
}
@@ -4690,11 +4693,7 @@ public:
}
case mbgl::MapChangeDidFinishLoadingStyle:
{
- [self.style willChangeValueForKey:@"name"];
- [self.style willChangeValueForKey:@"sources"];
- [self.style didChangeValueForKey:@"sources"];
- [self.style willChangeValueForKey:@"layers"];
- [self.style didChangeValueForKey:@"layers"];
+ self.style = [[MGLStyle alloc] initWithMapView:self];
if ([self.delegate respondsToSelector:@selector(mapView:didFinishLoadingStyle:)])
{
[self.delegate mapView:self didFinishLoadingStyle:self.style];
@@ -4805,7 +4804,12 @@ public:
else
{
CGRect adjustedFrame = annotationView.frame;
- adjustedFrame.origin.x = CGRectGetWidth(annotationView.layer.presentationLayer.frame) * -2.0;
+ if (annotationView.layer.presentationLayer) {
+ adjustedFrame.origin.x = -CGRectGetWidth(annotationView.layer.presentationLayer.frame) * 10.0;
+ } else {
+ // views that are added off screen do not have a presentationLayer
+ adjustedFrame.origin.x = -CGRectGetWidth(adjustedFrame) * 10.0;
+ }
annotationView.frame = adjustedFrame;
[self enqueueAnnotationViewForAnnotationContext:annotationContext];
}
@@ -4820,7 +4824,12 @@ public:
UIView <MGLCalloutView> *calloutView = self.calloutViewForSelectedAnnotation;
id <MGLAnnotation> annotation = calloutView.representedObject;
- if (calloutView && annotation)
+ BOOL isAnchoredToAnnotation = (calloutView
+ && annotation
+ && [calloutView respondsToSelector:@selector(isAnchoredToAnnotation)]
+ && calloutView.isAnchoredToAnnotation);
+
+ if (isAnchoredToAnnotation)
{
MGLAnnotationTag tag = [self annotationTagForAnnotation:annotation];
MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(tag);
diff --git a/platform/ios/src/MGLMapViewDelegate.h b/platform/ios/src/MGLMapViewDelegate.h
index 777af4ba63..09e2465a28 100644
--- a/platform/ios/src/MGLMapViewDelegate.h
+++ b/platform/ios/src/MGLMapViewDelegate.h
@@ -228,7 +228,22 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (nullable MGLAnnotationImage *)mapView:(MGLMapView *)mapView imageForAnnotation:(id <MGLAnnotation>)annotation;
-- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation __attribute__((deprecated("Use -mapView:strokeColorForShapeAnnotation: or -mapView:fillColorForPolygonAnnotation:.")));
+/**
+ Returns the alpha value to use when rendering a shape annotation.
+
+ A value of `0.0` results in a completely transparent shape. A value of `1.0`,
+ the default, results in a completely opaque shape.
+
+ This method sets the opacity of an entire shape, inclusive of its stroke and
+ fill. To independently set the values for stroke or fill, specify an alpha
+ component in the color returned by `-mapView:strokeColorForShapeAnnotation:` or
+ `-mapView:fillColorForPolygonAnnotation:`.
+
+ @param mapView The map view rendering the shape annotation.
+ @param annotation The annotation being rendered.
+ @return An alpha value between `0` and `1.0`.
+ */
+- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation;
/**
Returns the color to use when rendering the outline of a shape annotation.
diff --git a/platform/ios/src/MGLMapView_Private.h b/platform/ios/src/MGLMapView_Private.h
index 6b6a898f93..4e2765377c 100644
--- a/platform/ios/src/MGLMapView_Private.h
+++ b/platform/ios/src/MGLMapView_Private.h
@@ -1,11 +1,13 @@
#import <Mapbox/Mapbox.h>
-#import <mbgl/map/map.hpp>
+namespace mbgl {
+ class Map;
+}
/// Minimum size of an annotation’s accessibility element.
extern const CGSize MGLAnnotationAccessibilityElementMinimumSize;
-@interface MGLMapView (Internal)
+@interface MGLMapView (Private)
/// Currently shown popover representing the selected annotation.
@property (nonatomic) UIView<MGLCalloutView> *calloutViewForSelectedAnnotation;
diff --git a/platform/ios/src/MGLMapboxEvents.m b/platform/ios/src/MGLMapboxEvents.m
index 2c25cf0a62..744b80047b 100644
--- a/platform/ios/src/MGLMapboxEvents.m
+++ b/platform/ios/src/MGLMapboxEvents.m
@@ -278,6 +278,7 @@ const NSTimeInterval MGLFlushInterval = 180;
if (self.paused && enabled) {
[self resumeMetricsCollection];
} else if (!self.paused && !enabled) {
+ [self flush];
[self pauseMetricsCollection];
}
}
@@ -434,25 +435,31 @@ const NSTimeInterval MGLFlushInterval = 180;
}
- (MGLMapboxEventAttributes *)locationEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- MGLMutableMapboxEventAttributes *attributes = [@{MGLEventKeyEvent: MGLEventTypeLocation,
- MGLEventKeySource: MGLEventSource,
- MGLEventKeySessionId: self.instanceID,
- MGLEventKeyOperatingSystem: self.data.iOSVersion} mutableCopy];
- [self addApplicationStateToAttributes:attributes];
+ MGLMutableMapboxEventAttributes *attributes = [NSMutableDictionary dictionary];
+ attributes[MGLEventKeyEvent] = MGLEventTypeLocation;
+ attributes[MGLEventKeySource] = MGLEventSource;
+ attributes[MGLEventKeySessionId] = self.instanceID;
+ attributes[MGLEventKeyOperatingSystem] = self.data.iOSVersion;
+ NSString *currentApplicationState = [self applicationState];
+ if (![currentApplicationState isEqualToString:MGLApplicationStateUnknown]) {
+ attributes[MGLEventKeyApplicationState] = currentApplicationState;
+ }
+
return [self eventForAttributes:attributes attributeDictionary:attributeDictionary];
}
- (MGLMapboxEventAttributes *)mapLoadEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- MGLMutableMapboxEventAttributes *attributes = [@{MGLEventKeyEvent: MGLEventTypeMapLoad,
- MGLEventKeyCreated: [self.rfc3339DateFormatter stringFromDate:[NSDate date]],
- MGLEventKeyVendorID: self.data.vendorId,
- MGLEventKeyModel: self.data.model,
- MGLEventKeyOperatingSystem: self.data.iOSVersion,
- MGLEventKeyResolution: @(self.data.scale),
- MGLEventKeyAccessibilityFontScale: @([self contentSizeScale]),
- MGLEventKeyOrientation: [self deviceOrientation],
- MGLEventKeyWifi: @([[MGLReachability reachabilityForLocalWiFi] isReachableViaWiFi])} mutableCopy];
- [self addBatteryStateToAttributes:attributes];
+ MGLMutableMapboxEventAttributes *attributes = [NSMutableDictionary dictionary];
+ attributes[MGLEventKeyEvent] = MGLEventTypeMapLoad;
+ attributes[MGLEventKeyCreated] = [self.rfc3339DateFormatter stringFromDate:[NSDate date]];
+ attributes[MGLEventKeyVendorID] = self.data.vendorId;
+ attributes[MGLEventKeyModel] = self.data.model;
+ attributes[MGLEventKeyOperatingSystem] = self.data.iOSVersion;
+ attributes[MGLEventKeyResolution] = @(self.data.scale);
+ attributes[MGLEventKeyAccessibilityFontScale] = @([self contentSizeScale]);
+ attributes[MGLEventKeyOrientation] = [self deviceOrientation];
+ attributes[MGLEventKeyWifi] = @([[MGLReachability reachabilityForLocalWiFi] isReachableViaWiFi]);
+
return [self eventForAttributes:attributes attributeDictionary:attributeDictionary];
}
@@ -465,19 +472,22 @@ const NSTimeInterval MGLFlushInterval = 180;
- (MGLMapboxEventAttributes *)mapDragEndEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
MGLMutableMapboxEventAttributes *attributes = [self interactionEvent];
attributes[MGLEventKeyEvent] = MGLEventTypeMapDragEnd;
+
return [self eventForAttributes:attributes attributeDictionary:attributeDictionary];
}
- (MGLMutableMapboxEventAttributes *)interactionEvent {
- MGLMutableMapboxEventAttributes *attributes = [@{MGLEventKeyCreated: [self.rfc3339DateFormatter stringFromDate:[NSDate date]],
- MGLEventKeyOrientation: [self deviceOrientation],
- MGLEventKeyWifi: @([[MGLReachability reachabilityForLocalWiFi] isReachableViaWiFi])} mutableCopy];
- [self addBatteryStateToAttributes:attributes];
+ MGLMutableMapboxEventAttributes *attributes = [NSMutableDictionary dictionary];
+ attributes[MGLEventKeyCreated] = [self.rfc3339DateFormatter stringFromDate:[NSDate date]];
+ attributes[MGLEventKeyOrientation] = [self deviceOrientation];
+ attributes[MGLEventKeyWifi] = @([[MGLReachability reachabilityForLocalWiFi] isReachableViaWiFi]);
+
return attributes;
}
- (MGLMapboxEventAttributes *)eventForAttributes:(MGLMutableMapboxEventAttributes *)attributes attributeDictionary:(MGLMapboxEventAttributes *)attributeDictionary {
[attributes addEntriesFromDictionary:attributeDictionary];
+
return [attributes copy];
}
@@ -594,29 +604,6 @@ const NSTimeInterval MGLFlushInterval = 180;
return result;
}
-- (void)addBatteryStateToAttributes:(MGLMutableMapboxEventAttributes *)attributes {
- UIDeviceBatteryState batteryState = [[UIDevice currentDevice] batteryState];
- switch (batteryState) {
- case UIDeviceBatteryStateCharging:
- case UIDeviceBatteryStateFull:
- attributes[MGLEventKeyPluggedIn] = @(YES);
- break;
- case UIDeviceBatteryStateUnplugged:
- attributes[MGLEventKeyPluggedIn] = @(NO);
- break;
- default:
- // do nothing
- break;
- }
-}
-
-- (void)addApplicationStateToAttributes:(MGLMutableMapboxEventAttributes *)attributes {
- NSString *currentApplicationState = [self applicationState];
- if (![currentApplicationState isEqualToString:MGLApplicationStateUnknown]) {
- attributes[MGLEventKeyApplicationState] = currentApplicationState;
- }
-}
-
+ (void)ensureMetricsOptoutExists {
NSNumber *shownInAppNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"MGLMapboxMetricsEnabledSettingShownInApp"];
BOOL metricsEnabledSettingShownInAppFlag = [shownInAppNumber boolValue];
diff --git a/platform/ios/src/MGLUserLocation.h b/platform/ios/src/MGLUserLocation.h
index f2243815cf..1a27d31dd4 100644
--- a/platform/ios/src/MGLUserLocation.h
+++ b/platform/ios/src/MGLUserLocation.h
@@ -11,7 +11,7 @@ NS_ASSUME_NONNULL_BEGIN
directly. Instead, you retrieve an existing MGLUserLocation object from the
`userLocation` property of the map view displayed in your application.
*/
-@interface MGLUserLocation : NSObject <MGLAnnotation>
+@interface MGLUserLocation : NSObject <MGLAnnotation, NSSecureCoding>
#pragma mark Determining the User’s Position
diff --git a/platform/ios/src/MGLUserLocation.m b/platform/ios/src/MGLUserLocation.m
index a568ec8be1..97e3f740fc 100644
--- a/platform/ios/src/MGLUserLocation.m
+++ b/platform/ios/src/MGLUserLocation.m
@@ -26,6 +26,44 @@ NS_ASSUME_NONNULL_END
return self;
}
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ if (self = [super init]) {
+ _location = [decoder decodeObjectOfClass:[CLLocation class] forKey:@"location"];
+ _title = [decoder decodeObjectOfClass:[NSString class] forKey:@"title"];
+ _subtitle = [decoder decodeObjectOfClass:[NSString class] forKey:@"subtitle"];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [coder encodeObject:_location forKey:@"location"];
+ [coder encodeObject:_title forKey:@"title"];
+ [coder encodeObject:_subtitle forKey:@"subtitle"];
+}
+
+- (BOOL)isEqual:(id)other {
+ if (self == other) return YES;
+ if (![other isKindOfClass:[MGLUserLocation class]]) return NO;
+
+ MGLUserLocation *otherUserLocation = other;
+ return ((!self.location && !otherUserLocation.location) || [self.location distanceFromLocation:otherUserLocation.location] == 0)
+ && ((!self.title && !otherUserLocation.title) || [self.title isEqualToString:otherUserLocation.title])
+ && ((!self.subtitle && !otherUserLocation.subtitle) || [self.subtitle isEqualToString:otherUserLocation.subtitle]);
+}
+
+- (NSUInteger)hash {
+ NSUInteger hash = [super hash];
+ hash += [_location hash];
+ hash += [_heading hash];
+ hash += [_title hash];
+ hash += [_subtitle hash];
+ return hash;
+}
+
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
{
return ! [key isEqualToString:@"location"] && ! [key isEqualToString:@"heading"];
diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h
index 88efdd5cb4..37c649781e 100644
--- a/platform/ios/src/Mapbox.h
+++ b/platform/ios/src/Mapbox.h
@@ -54,6 +54,5 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "MGLUserLocation.h"
#import "MGLUserLocationAnnotationView.h"
#import "NSValue+MGLAdditions.h"
-#import "NSValue+MGLStyleEnumAttributeAdditions.h"
#import "MGLStyleValue.h"
#import "MGLAttributionInfo.h"