summaryrefslogtreecommitdiff
path: root/platform/ios/src
diff options
context:
space:
mode:
Diffstat (limited to 'platform/ios/src')
-rw-r--r--platform/ios/src/MGLAnnotationView.mm5
-rw-r--r--platform/ios/src/MGLMapView.h110
-rw-r--r--platform/ios/src/MGLMapView.mm71
-rw-r--r--platform/ios/src/MGLMapViewDelegate.h20
-rw-r--r--platform/ios/src/MGLSDKUpdateChecker.h13
-rw-r--r--platform/ios/src/MGLSDKUpdateChecker.mm39
6 files changed, 185 insertions, 73 deletions
diff --git a/platform/ios/src/MGLAnnotationView.mm b/platform/ios/src/MGLAnnotationView.mm
index 0e904779d5..5e0ae3b848 100644
--- a/platform/ios/src/MGLAnnotationView.mm
+++ b/platform/ios/src/MGLAnnotationView.mm
@@ -239,7 +239,10 @@
}
else if (dragState == MGLAnnotationViewDragStateCanceling)
{
- NSAssert(self.annotation, @"Annotation property should not be nil.");
+ if (!self.annotation) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Annotation property should not be nil."];
+ }
self.panGestureRecognizer.enabled = NO;
self.longPressRecognizer.enabled = NO;
self.center = [self.mapView convertCoordinate:self.annotation.coordinate toPointToView:self.mapView];
diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h
index 8fb58005de..4872ff2448 100644
--- a/platform/ios/src/MGLMapView.h
+++ b/platform/ios/src/MGLMapView.h
@@ -45,6 +45,40 @@ typedef NS_ENUM(NSUInteger, MGLAnnotationVerticalAlignment) {
};
/**
+ The mode used to track the user location on the map. Used with
+ `MGLMapView.userTrackingMode`.
+ */
+typedef NS_ENUM(NSUInteger, MGLUserTrackingMode) {
+ /** The map does not follow the user location. */
+ MGLUserTrackingModeNone = 0,
+ /** The map follows the user location. This tracking mode falls back
+ to `MGLUserTrackingModeNone` if the user pans the map view. */
+ MGLUserTrackingModeFollow,
+ /**
+ The map follows the user location and rotates when the heading changes.
+ The default user location annotation displays a fan-shaped indicator with
+ the current heading. The heading indicator represents the direction the
+ device is facing, which is sized according to the reported accuracy.
+
+ This tracking mode is disabled if the user pans the map view, but
+ remains enabled if the user zooms in. If the user rotates the map
+ view, this tracking mode will fall back to `MGLUserTrackingModeFollow`.
+ */
+ MGLUserTrackingModeFollowWithHeading,
+ /**
+ The map follows the user location and rotates when the course changes.
+ Course represents the direction in which the device is traveling.
+ The default user location annotation shows a puck-shaped indicator
+ that rotates as the course changes.
+
+ This tracking mode is disabled if the user pans the map view, but
+ remains enabled if the user zooms in. If the user rotates the map view,
+ this tracking mode will fall back to `MGLUserTrackingModeFollow`.
+ */
+ MGLUserTrackingModeFollowWithCourse,
+};
+
+/**
An interactive, customizable map view with an interface similar to the one
provided by Apple’s MapKit.
@@ -1137,10 +1171,11 @@ IB_DESIGNABLE
Returns an array of rendered map features that intersect with a given point,
restricted to the given style layers.
- This method may return all features from the specified layers. To filter
- the returned features, use the
- `-visibleFeaturesAtPoint:inStyleLayersWithIdentifiers:predicate:` method. For more
- information about searching for map features, see that method’s documentation.
+ This method returns all the intersecting features from the specified layers. To
+ filter the returned features, use the
+ `-visibleFeaturesAtPoint:inStyleLayersWithIdentifiers:predicate:` method. For
+ more information about searching for map features, see that method’s
+ documentation.
@param point A point expressed in the map view’s coordinate system.
@param styleLayerIdentifiers A set of strings that correspond to the names
@@ -1153,17 +1188,17 @@ IB_DESIGNABLE
/**
Returns an array of rendered map features that intersect with a given point,
- restricted to the given style layers and filtered by the given
- predicate.
+ restricted to the given style layers and filtered by the given predicate.
Each object in the returned array represents a feature rendered by the
- current style and provides access to attributes specified by the relevant
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile sources</a>.
- The returned array includes features specified in vector and GeoJSON tile
- sources but does not include anything from raster, image, or video sources.
-
- Only visible features are returned. For example, suppose the current style uses
- the
+ current style and provides access to attributes specified by the relevant map
+ content sources. The returned array includes features loaded by
+ `MGLShapeSource` and `MGLVectorSource` objects but does not include anything
+ from `MGLRasterSource` objects, or from image, video, or canvas sources, which
+ are unsupported by this SDK.
+
+ The returned features are drawn by a style layer in the current style. For
+ example, suppose the current style uses the
<a href="https://www.mapbox.com/vector-tiles/mapbox-streets/">Mapbox Streets source</a>,
but none of the specified style layers includes features that have the `maki`
property set to `bus`. If you pass a point corresponding to the location of a
@@ -1190,13 +1225,18 @@ IB_DESIGNABLE
To find out the layer names in a particular style, view the style in
<a href="https://www.mapbox.com/studio/">Mapbox Studio</a>.
+ Only visible features are returned. To obtain features regardless of
+ visibility, use the
+ `-[MGLVectorSource featuresInSourceLayersWithIdentifiers:predicate:]` and
+ `-[MGLShapeSource featuresMatchingPredicate:]` methods on the relevant sources.
+
@note Layer identifiers are not guaranteed to exist across styles or different
- versions of the same style. Applications that use this API must first set the
- style URL to an explicitly versioned style using a convenience method like
- `+[MGLStyle outdoorsStyleURLWithVersion:]`, `MGLMapView`’s “Style URL”
+ versions of the same style. Applications that use this API must first set
+ the style URL to an explicitly versioned style using a convenience method
+ like `+[MGLStyle outdoorsStyleURLWithVersion:]`, `MGLMapView`’s “Style URL”
inspectable in Interface Builder, or a manually constructed `NSURL`. This
- approach also avoids layer identifer name changes that will occur in the default
- style’s layers over time.
+ approach also avoids layer identifer name changes that will occur in the
+ default style’s layers over time.
@param point A point expressed in the map view’s coordinate system.
@param styleLayerIdentifiers A set of strings that correspond to the names of
@@ -1227,9 +1267,9 @@ IB_DESIGNABLE
Returns an array of rendered map features that intersect with the given
rectangle, restricted to the given style layers.
- This method may return all features from the specified layers. To filter
- the returned features, use the
- `-visibleFeaturesAtPoint:inStyleLayersWithIdentifiers:predicate:` method. For
+ This method returns all the intersecting features from the specified layers. To
+ filter the returned features, use the
+ `-visibleFeaturesAtPoint:inStyleLayersWithIdentifiers:predicate:` method. For
more information about searching for map features, see that method’s
documentation.
@@ -1248,13 +1288,14 @@ IB_DESIGNABLE
predicate.
Each object in the returned array represents a feature rendered by the
- current style and provides access to attributes specified by the relevant
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile sources</a>.
- The returned array includes features specified in vector and GeoJSON tile
- sources but does not include anything from raster, image, or video sources.
-
- Only visible features are returned. For example, suppose the current style uses
- the
+ current style and provides access to attributes specified by the relevant map
+ content sources. The returned array includes features loaded by
+ `MGLShapeSource` and `MGLVectorSource` objects but does not include anything
+ from `MGLRasterSource` objects, or from image, video, or canvas sources, which
+ are unsupported by this SDK.
+
+ The returned features are drawn by a style layer in the current style. For
+ example, suppose the current style uses the
<a href="https://www.mapbox.com/vector-tiles/mapbox-streets/">Mapbox Streets source</a>,
but none of the specified style layers includes features that have the `maki`
property set to `bus`. If you pass a rectangle containing the location of a bus
@@ -1282,6 +1323,11 @@ IB_DESIGNABLE
To find out the layer names in a particular style, view the style in
<a href="https://www.mapbox.com/studio/">Mapbox Studio</a>.
+ Only visible features are returned. To obtain features regardless of
+ visibility, use the
+ `-[MGLVectorSource featuresInSourceLayersWithIdentifiers:predicate:]` and
+ `-[MGLShapeSource featuresMatchingPredicate:]` methods on the relevant sources.
+
@note Layer identifiers are not guaranteed to exist across styles or different
versions of the same style. Applications that use this API must first set the
style URL to an explicitly versioned style using a convenience method like
@@ -1290,6 +1336,14 @@ IB_DESIGNABLE
approach also avoids layer identifer name changes that will occur in the default
style’s layers over time.
+ @note Layer identifiers are not guaranteed to exist across styles or different
+ versions of the same style. Applications that use this API must first set
+ the style URL to an explicitly versioned style using a convenience method
+ like `+[MGLStyle outdoorsStyleURLWithVersion:]`, `MGLMapView`’s “Style URL”
+ inspectable in Interface Builder, or a manually constructed `NSURL`. This
+ approach also avoids layer identifer name changes that will occur in the
+ default style’s layers over time.
+
@param rect A rectangle expressed in the map view’s coordinate system.
@param styleLayerIdentifiers A set of strings that correspond to the names of
layers defined in the current style. Only the features contained in these
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 257167227f..1ccd5ce355 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -58,6 +58,7 @@
#import "MGLStyle_Private.h"
#import "MGLStyleLayer_Private.h"
#import "MGLMapboxEvents.h"
+#import "MGLSDKUpdateChecker.h"
#import "MGLCompactCalloutView.h"
#import "MGLAnnotationContainerView.h"
#import "MGLAnnotationContainerView_Private.h"
@@ -350,6 +351,14 @@ public:
return self;
}
++ (void)initialize
+{
+ if (self == [MGLMapView self])
+ {
+ [MGLSDKUpdateChecker checkForUpdates];
+ }
+}
+
+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyle
{
return [NSSet setWithObject:@"styleURL"];
@@ -533,14 +542,11 @@ public:
_decelerationRate = MGLMapViewDecelerationRateNormal;
- if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
- {
- _quickZoom = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleQuickZoomGesture:)];
- _quickZoom.numberOfTapsRequired = 1;
- _quickZoom.minimumPressDuration = 0;
- [_quickZoom requireGestureRecognizerToFail:doubleTap];
- [self addGestureRecognizer:_quickZoom];
- }
+ _quickZoom = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleQuickZoomGesture:)];
+ _quickZoom.numberOfTapsRequired = 1;
+ _quickZoom.minimumPressDuration = 0;
+ [_quickZoom requireGestureRecognizerToFail:doubleTap];
+ [self addGestureRecognizer:_quickZoom];
// observe app activity
//
@@ -1242,7 +1248,7 @@ public:
if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
[self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
- _mbglMap->moveBy({ offset.x, offset.y }, MGLDurationInSecondsFromTimeInterval(self.decelerationRate));
+ _mbglMap->moveBy({ offset.x, offset.y }, MGLDurationFromTimeInterval(self.decelerationRate));
}
}
@@ -1351,7 +1357,7 @@ public:
} else {
if (drift)
{
- _mbglMap->setScale(newScale, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationInSecondsFromTimeInterval(duration));
+ _mbglMap->setScale(newScale, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationFromTimeInterval(duration));
}
}
@@ -1422,7 +1428,7 @@ public:
if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
[self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
- _mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationInSecondsFromTimeInterval(decelerationRate));
+ _mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationFromTimeInterval(decelerationRate));
[self notifyGestureDidEndWithDrift:YES];
@@ -1513,14 +1519,18 @@ public:
return nil;
}
}
-
+
// Handle the case of an offset annotation view by converting the tap point to be the geo location
// of the annotation itself that the view represents
for (MGLAnnotationView *view in self.annotationContainerView.annotationViews)
{
if (view.centerOffset.dx != 0 || view.centerOffset.dy != 0) {
if (CGRectContainsPoint(view.frame, tapPoint)) {
- NSAssert(view.annotation, @"Annotation's view annotation property should not be nil.");
+ if (!view.annotation) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Annotation view's annotation property should not be nil."];
+ }
+
CGPoint annotationPoint = [self convertCoordinate:view.annotation.coordinate toPointToView:self];
tapPoint = annotationPoint;
}
@@ -1563,7 +1573,7 @@ public:
[self trackGestureEvent:MGLEventGestureDoubleTap forRecognizer:doubleTap];
mbgl::ScreenCoordinate center(gesturePoint.x, gesturePoint.y);
- _mbglMap->setZoom(newZoom, center, MGLDurationInSecondsFromTimeInterval(MGLAnimationDuration));
+ _mbglMap->setZoom(newZoom, center, MGLDurationFromTimeInterval(MGLAnimationDuration));
__weak MGLMapView *weakSelf = self;
@@ -1601,7 +1611,7 @@ public:
[self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
mbgl::ScreenCoordinate center(gesturePoint.x, gesturePoint.y);
- _mbglMap->setZoom(newZoom, center, MGLDurationInSecondsFromTimeInterval(MGLAnimationDuration));
+ _mbglMap->setZoom(newZoom, center, MGLDurationFromTimeInterval(MGLAnimationDuration));
__weak MGLMapView *weakSelf = self;
@@ -1991,10 +2001,7 @@ public:
// Update the annotation’s backing geometry to match the annotation model object. Any associated annotation view is also moved by side effect. However, -updateAnnotationViews disables the view’s animation actions, because it can’t distinguish between moves due to the viewport changing and moves due to the annotation’s coordinate changing.
_mbglMap->updateAnnotation(annotationTag, mbgl::SymbolAnnotation { point, symbolName.UTF8String });
- if (annotationTag == _selectedAnnotationTag)
- {
- [self deselectAnnotation:annotation animated:YES];
- }
+ [self updateCalloutView];
}
}
else if ([keyPath isEqualToString:@"coordinates"] && [object isKindOfClass:[MGLMultiPoint class]])
@@ -2011,13 +2018,7 @@ public:
{
// Update the annotation’s backing geometry to match the annotation model object.
_mbglMap->updateAnnotation(annotationTag, [annotation annotationObjectWithDelegate:self]);
-
- // We don't current support shape multipoint annotation selection, but let's make sure
- // deselection is handled just to avoid problems in the future.
- if (annotationTag == _selectedAnnotationTag)
- {
- [self deselectAnnotation:annotation animated:YES];
- }
+ [self updateCalloutView];
}
}
}
@@ -2431,7 +2432,7 @@ public:
mbgl::AnimationOptions animationOptions;
if (duration)
{
- animationOptions.duration.emplace(MGLDurationInSecondsFromTimeInterval(duration));
+ animationOptions.duration.emplace(MGLDurationFromTimeInterval(duration));
animationOptions.easing.emplace(MGLUnitBezierForMediaTimingFunction(function));
}
if (completion)
@@ -2486,7 +2487,7 @@ public:
_mbglMap->setZoom(zoomLevel,
MGLEdgeInsetsFromNSEdgeInsets(self.contentInset),
- MGLDurationInSecondsFromTimeInterval(duration));
+ MGLDurationFromTimeInterval(duration));
}
- (void)setMinimumZoomLevel:(double)minimumZoomLevel
@@ -2595,7 +2596,7 @@ public:
mbgl::AnimationOptions animationOptions;
if (duration > 0)
{
- animationOptions.duration.emplace(MGLDurationInSecondsFromTimeInterval(duration));
+ animationOptions.duration.emplace(MGLDurationFromTimeInterval(duration));
animationOptions.easing.emplace(MGLUnitBezierForMediaTimingFunction(function));
}
if (completion)
@@ -2658,13 +2659,13 @@ public:
{
_mbglMap->setBearing(direction,
MGLEdgeInsetsFromNSEdgeInsets(self.contentInset),
- MGLDurationInSecondsFromTimeInterval(duration));
+ MGLDurationFromTimeInterval(duration));
}
else
{
CGPoint centerPoint = self.userLocationAnnotationViewCenter;
_mbglMap->setBearing(direction, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y },
- MGLDurationInSecondsFromTimeInterval(duration));
+ MGLDurationFromTimeInterval(duration));
}
}
@@ -2709,7 +2710,7 @@ public:
mbgl::AnimationOptions animationOptions;
if (duration > 0)
{
- animationOptions.duration.emplace(MGLDurationInSecondsFromTimeInterval(duration));
+ animationOptions.duration.emplace(MGLDurationFromTimeInterval(duration));
animationOptions.easing.emplace(MGLUnitBezierForMediaTimingFunction(function));
}
if (completion)
@@ -2759,7 +2760,7 @@ public:
mbgl::AnimationOptions animationOptions;
if (duration >= 0)
{
- animationOptions.duration = MGLDurationInSecondsFromTimeInterval(duration);
+ animationOptions.duration = MGLDurationFromTimeInterval(duration);
}
if (peakAltitude >= 0)
{
@@ -3067,7 +3068,7 @@ public:
return annotationContext.annotation;
}
-/// Returns the annotation tag assigned to the given annotation. Relatively expensive.
+/// Returns the annotation tag assigned to the given annotation.
- (MGLAnnotationTag)annotationTagForAnnotation:(id <MGLAnnotation>)annotation
{
if ( ! annotation || annotation == self.userLocation
@@ -3107,7 +3108,7 @@ public:
NSAssert([annotation conformsToProtocol:@protocol(MGLAnnotation)], @"annotation should conform to MGLAnnotation");
// adding the same annotation object twice is a no-op
- if ([self.annotations containsObject:annotation])
+ if (_annotationTagsByAnnotation.count(annotation) != 0)
{
continue;
}
diff --git a/platform/ios/src/MGLMapViewDelegate.h b/platform/ios/src/MGLMapViewDelegate.h
index 12320a63a5..c1faaa3d07 100644
--- a/platform/ios/src/MGLMapViewDelegate.h
+++ b/platform/ios/src/MGLMapViewDelegate.h
@@ -22,12 +22,11 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark Responding to Map Position Changes
/**
- Tells the delegate that the viewpoint depicted by the map view is about to
- change.
-
+ Tells the delegate that the viewpoint depicted by the map view is about to change.
+
This method is called whenever the currently displayed map camera will start
changing for any reason.
-
+
@param mapView The map view whose viewpoint will change.
@param animated Whether the change will cause an animated effect on the map.
*/
@@ -38,10 +37,12 @@ NS_ASSUME_NONNULL_BEGIN
This method is called as the currently displayed map camera changes as part of
an animation, whether due to a user gesture or due to a call to a method such
- as `-[MGLMapView setCamera:animated:]`. During the animation, this method may
- be called many times to report updates to the viewpoint. Therefore, your
- implementation of this method should be as lightweight as possible to avoid
- affecting performance.
+ as `-[MGLMapView setCamera:animated:]`. This method can be called before
+ `-mapViewDidFinishLoadingMap:` is called.
+
+ During the animation, this method may be called many times to report updates to
+ the viewpoint. Therefore, your implementation of this method should be as lightweight
+ as possible to avoid affecting performance.
@param mapView The map view whose viewpoint is changing.
*/
@@ -52,7 +53,8 @@ NS_ASSUME_NONNULL_BEGIN
changing.
This method is called whenever the currently displayed map camera has finished
- changing, after any calls to `-mapViewRegionIsChanging:` due to animation.
+ changing, after any calls to `-mapViewRegionIsChanging:` due to animation. Therefore,
+ this method can be called before `-mapViewDidFinishLoadingMap:` is called.
@param mapView The map view whose viewpoint has changed.
@param animated Whether the change caused an animated effect on the map.
diff --git a/platform/ios/src/MGLSDKUpdateChecker.h b/platform/ios/src/MGLSDKUpdateChecker.h
new file mode 100644
index 0000000000..13cef46ad4
--- /dev/null
+++ b/platform/ios/src/MGLSDKUpdateChecker.h
@@ -0,0 +1,13 @@
+#import <Foundation/Foundation.h>
+
+#import "MGLFoundation.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MGLSDKUpdateChecker : NSObject
+
++ (void)checkForUpdates;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/ios/src/MGLSDKUpdateChecker.mm b/platform/ios/src/MGLSDKUpdateChecker.mm
new file mode 100644
index 0000000000..ab4ef7be86
--- /dev/null
+++ b/platform/ios/src/MGLSDKUpdateChecker.mm
@@ -0,0 +1,39 @@
+#import "MGLSDKUpdateChecker.h"
+#import "NSBundle+MGLAdditions.h"
+#import "NSProcessInfo+MGLAdditions.h"
+
+@implementation MGLSDKUpdateChecker
+
++ (void)checkForUpdates {
+#if TARGET_IPHONE_SIMULATOR
+ // Abort if running in a playground.
+ if ([[NSBundle mainBundle].bundleIdentifier hasPrefix:@"com.apple.dt.playground."] ||
+ NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent) {
+ return;
+ }
+
+ NSString *currentVersion = [NSBundle mgl_frameworkInfoDictionary][@"MGLSemanticVersionString"];
+
+ // Skip version check if weʼre doing gl-native development, as the framework
+ // version is `1` until built for packaging.
+ if ([currentVersion isEqualToString:@"1.0.0"]) {
+ return;
+ }
+
+ NSURL *url = [NSURL URLWithString:@"https://www.mapbox.com/ios-sdk/latest_version"];
+ [[NSURLSession.sharedSession dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+ if (error || ((NSHTTPURLResponse *)response).statusCode != 200) {
+ return;
+ }
+
+ NSString *latestVersion = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+ latestVersion = [latestVersion stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ if (![currentVersion isEqualToString:latestVersion]) {
+ NSString *updateAvailable = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"SDK_UPDATE_AVAILABLE", nil, nil, @"Mapbox iOS SDK version %@ is now available:", @"Developer-only SDK update notification; {latest version, in format x.x.x}"), latestVersion];
+ NSLog(@"%@ https://github.com/mapbox/mapbox-gl-native/releases/tag/ios-v%@", updateAvailable, latestVersion);
+ }
+ }] resume];
+#endif
+}
+
+@end