summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--platform/ios/CHANGELOG.md6
-rw-r--r--platform/ios/Integration Test Harness/Info.plist2
-rw-r--r--platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m26
-rw-r--r--platform/ios/Integration Tests/MGLMapViewIntegrationTest.h1
-rw-r--r--platform/ios/Integration Tests/MGLMapViewIntegrationTest.m7
-rw-r--r--platform/ios/Integration Tests/MGLTestLocationManager.h10
-rw-r--r--platform/ios/Integration Tests/MGLTestLocationManager.m44
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj7
-rw-r--r--platform/ios/src/MGLMapView.h17
-rw-r--r--platform/ios/src/MGLMapView.mm18
-rw-r--r--platform/ios/src/MGLMapViewDelegate.h15
-rw-r--r--platform/ios/test/MGLMapViewDelegateIntegrationTests.swift2
12 files changed, 147 insertions, 8 deletions
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index 9611ec9ae2..7693dca330 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -11,6 +11,12 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
* Fixed a crash when a style layer `*-pattern` property evaluates to nil for a particular feature. ([#12896](https://github.com/mapbox/mapbox-gl-native/pull/12896))
* Fixed an issue with view annotations (including the user location annotation) and the GL map lagging relative to each other. ([#12895](https://github.com/mapbox/mapbox-gl-native/pull/12895))
+### User location
+* Added `-[MGLMapViewDelegate mapViewUserLocationAnchorPoint:]` to customize the position of the user location annotation.. ([#12907](https://github.com/mapbox/mapbox-gl-native/pull/12907))
+* Marked `-[MGLMapView setUserLocationVerticalAlignment:]` as deprecated. Use `-[MGLMapViewDelegate mapViewUserLocationAnchorPoint:]` instead. ([#12907](https://github.com/mapbox/mapbox-gl-native/pull/12907))
+* Added the `-[MGLMapView updateUserLocationAnnotationView]` and `-[MGLMapView updateUserLocationAnnotationView:animated:]` methods to update the position of the user location annotation between location updates. ([#12907](https://github.com/mapbox/mapbox-gl-native/pull/12907))
+* Fixed an issue where the user location annotation was positioned incorrectly when the map view had a left or right content inset. ([#12907](https://github.com/mapbox/mapbox-gl-native/pull/12907)) inset
+
### Offline maps
* Added `-[MGLOfflineStorage addContentsOfFile:withCompletionHandler:]` and `-[MGLOfflineStorage addContentsOfURL:withCompletionHandler:]` methods to add pregenerated offline packs to offline storage. ([#12791](https://github.com/mapbox/mapbox-gl-native/pull/12791))
diff --git a/platform/ios/Integration Test Harness/Info.plist b/platform/ios/Integration Test Harness/Info.plist
index 4222ac2dd3..be30bb5cc5 100644
--- a/platform/ios/Integration Test Harness/Info.plist
+++ b/platform/ios/Integration Test Harness/Info.plist
@@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
+ <key>NSLocationWhenInUseUsageDescription</key>
+ <string>Used to run user location tests</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
diff --git a/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m b/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m
index ba63a9eff4..fefb938773 100644
--- a/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m
+++ b/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m
@@ -1,6 +1,7 @@
#import "MGLMapViewIntegrationTest.h"
#import "MGLTestUtility.h"
#import "MGLMapAccessibilityElement.h"
+#import "MGLTestLocationManager.h"
@interface MGLMapView (Tests)
- (MGLAnnotationTag)annotationTagAtPoint:(CGPoint)point persistingResults:(BOOL)persist;
@@ -91,6 +92,31 @@
}
}
+- (void)testUserLocationWithOffsetAnchorPoint {
+ [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(37.787357, -122.39899)];
+ MGLTestLocationManager *locationManager = [[MGLTestLocationManager alloc] init];
+ self.mapView.locationManager = locationManager;
+
+ [self.mapView setUserTrackingMode:MGLUserTrackingModeFollow animated:NO];
+ CGRect originalFrame = [self.mapView viewForAnnotation:self.mapView.userLocation].frame;
+
+ // Temporarily disable location tracking so we can save the value of
+ // the originalFrame in memory
+ [self.mapView setUserTrackingMode:MGLUserTrackingModeNone animated:NO];
+
+ CGPoint offset = CGPointMake(20, 20);
+
+ self.mapViewUserLocationAnchorPoint = ^CGPoint (MGLMapView *mapView) {
+ return offset;;
+ };
+
+ [self.mapView setUserTrackingMode:MGLUserTrackingModeFollow animated:NO];
+ CGRect offsetFrame = [self.mapView viewForAnnotation:self.mapView.userLocation].frame;
+
+ XCTAssertEqual(originalFrame.origin.x + offset.x, offsetFrame.origin.x);
+ XCTAssertEqual(originalFrame.origin.y + offset.y, offsetFrame.origin.y);
+}
+
- (void)waitForCollisionDetectionToRun {
XCTAssertNil(self.renderFinishedExpectation, @"Incorrect test setup");
diff --git a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h
index f513df8b20..2f459a5f44 100644
--- a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h
+++ b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h
@@ -26,6 +26,7 @@
@property (nonatomic) void (^regionWillChange)(MGLMapView *mapView, BOOL animated);
@property (nonatomic) void (^regionIsChanging)(MGLMapView *mapView);
@property (nonatomic) void (^regionDidChange)(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated);
+@property (nonatomic) CGPoint (^mapViewUserLocationAnchorPoint)(MGLMapView *mapView);
// Utility methods
- (NSString*)validAccessToken;
diff --git a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m
index c427a7842f..7153257df5 100644
--- a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m
+++ b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m
@@ -101,6 +101,13 @@
}
}
+- (CGPoint)mapViewUserLocationAnchorPoint:(MGLMapView *)mapView {
+ if (self.mapViewUserLocationAnchorPoint) {
+ return self.mapViewUserLocationAnchorPoint(mapView);
+ }
+ return CGPointZero;
+}
+
#pragma mark - Utilities
- (void)waitForMapViewToFinishLoadingStyleWithTimeout:(NSTimeInterval)timeout {
diff --git a/platform/ios/Integration Tests/MGLTestLocationManager.h b/platform/ios/Integration Tests/MGLTestLocationManager.h
new file mode 100644
index 0000000000..e0e6f25bb2
--- /dev/null
+++ b/platform/ios/Integration Tests/MGLTestLocationManager.h
@@ -0,0 +1,10 @@
+#import <XCTest/XCTest.h>
+#import <Mapbox/Mapbox.h>
+#import "MGLTestUtility.h"
+
+@interface MGLTestLocationManager : NSObject<MGLLocationManager>
+@end
+
+@interface MGLTestLocationManager()
+
+@end
diff --git a/platform/ios/Integration Tests/MGLTestLocationManager.m b/platform/ios/Integration Tests/MGLTestLocationManager.m
new file mode 100644
index 0000000000..f9a5a8650f
--- /dev/null
+++ b/platform/ios/Integration Tests/MGLTestLocationManager.m
@@ -0,0 +1,44 @@
+#import "MGLTestLocationManager.h"
+
+// Used to supply integration tests with a simulated location manager.
+// Methods that are empty are not used within integration tests and are
+// therefore unimplemented.
+
+@implementation MGLTestLocationManager
+
+@synthesize delegate;
+
+- (CLAuthorizationStatus)authorizationStatus { return kCLAuthorizationStatusAuthorizedAlways; }
+
+- (void)setHeadingOrientation:(CLDeviceOrientation)headingOrientation { }
+
+- (CLDeviceOrientation)headingOrientation { return 90; }
+
+- (void)requestAlwaysAuthorization { }
+
+- (void)requestWhenInUseAuthorization { }
+
+- (void)startUpdatingHeading { }
+
+// Simulate one location update
+- (void)startUpdatingLocation
+{
+ if ([self.delegate respondsToSelector:@selector(locationManager:didUpdateLocations:)]) {
+ CLLocation *location = [[CLLocation alloc] initWithLatitude:37.787357 longitude:-122.39899];
+ [self.delegate locationManager:self didUpdateLocations:@[location]];
+ }
+}
+
+- (void)stopUpdatingHeading { }
+
+- (void)stopUpdatingLocation { }
+
+- (void)dismissHeadingCalibrationDisplay { }
+
+- (void)dealloc { self.delegate = nil; }
+
+- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading { }
+
+- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager { return NO; }
+
+@end
diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
index bacfafa207..22c416d928 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -14,6 +14,8 @@
071BBB071EE77631001FB02A /* MGLImageSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 071BBB051EE7761A001FB02A /* MGLImageSourceTests.m */; };
076171C32139C70900668A35 /* MGLMapViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 076171C22139C70900668A35 /* MGLMapViewTests.m */; };
076171C72141A91700668A35 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 076171C62141A91700668A35 /* Settings.bundle */; };
+ 077061D6215D97EF000FEF62 /* simple_route.json in Resources */ = {isa = PBXBuildFile; fileRef = 1F26B6C220E1A351007BCC21 /* simple_route.json */; };
+ 077061DA215DA00E000FEF62 /* MGLTestLocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 077061D9215DA00E000FEF62 /* MGLTestLocationManager.m */; };
0778DD431F67556700A73B34 /* MGLComputedShapeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 0778DD401F67555F00A73B34 /* MGLComputedShapeSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
0778DD441F67556C00A73B34 /* MGLComputedShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */; };
07D8C6FB1F67560100381808 /* MGLComputedShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */; };
@@ -747,6 +749,8 @@
071BBB051EE7761A001FB02A /* MGLImageSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLImageSourceTests.m; path = ../../darwin/test/MGLImageSourceTests.m; sourceTree = "<group>"; };
076171C22139C70900668A35 /* MGLMapViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MGLMapViewTests.m; path = ../../darwin/test/MGLMapViewTests.m; sourceTree = "<group>"; };
076171C62141A91700668A35 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = Settings.bundle; path = app/Settings.bundle; sourceTree = SOURCE_ROOT; };
+ 077061D9215DA00E000FEF62 /* MGLTestLocationManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLTestLocationManager.m; sourceTree = "<group>"; };
+ 077061DB215DA11F000FEF62 /* MGLTestLocationManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLTestLocationManager.h; sourceTree = "<group>"; };
0778DD401F67555F00A73B34 /* MGLComputedShapeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLComputedShapeSource.h; sourceTree = "<group>"; };
0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLComputedShapeSource.mm; sourceTree = "<group>"; };
07D8C6FD1F67562800381808 /* MGLComputedShapeSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLComputedShapeSourceTests.m; path = ../../darwin/test/MGLComputedShapeSourceTests.m; sourceTree = "<group>"; };
@@ -1376,6 +1380,8 @@
CA0C27932076CA19001CE5B7 /* MGLMapViewIntegrationTest.m */,
CA0C27952076CA50001CE5B7 /* MGLMapViewIntegrationTest.h */,
CA4EB8C620863487006AB465 /* MGLStyleLayerIntegrationTests.m */,
+ 077061DB215DA11F000FEF62 /* MGLTestLocationManager.h */,
+ 077061D9215DA00E000FEF62 /* MGLTestLocationManager.m */,
);
path = "Integration Tests";
sourceTree = "<group>";
@@ -2825,6 +2831,7 @@
CA0C27942076CA19001CE5B7 /* MGLMapViewIntegrationTest.m in Sources */,
CAE7AD5520F46EF5003B6782 /* MGLMapSnapshotterSwiftTests.swift in Sources */,
CA0C27922076C804001CE5B7 /* MGLShapeSourceTests.m in Sources */,
+ 077061DA215DA00E000FEF62 /* MGLTestLocationManager.m in Sources */,
CA6914B520E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.m in Sources */,
CA1B4A512099FB2200EDD491 /* MGLMapSnapshotterTest.m in Sources */,
);
diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h
index 02d146edcb..5312804cd2 100644
--- a/platform/ios/src/MGLMapView.h
+++ b/platform/ios/src/MGLMapView.h
@@ -416,7 +416,7 @@ MGL_EXPORT IB_DESIGNABLE
transition. If you don’t want to animate the change, use the
`-setUserLocationVerticalAlignment:animated:` method instead.
*/
-@property (nonatomic, assign) MGLAnnotationVerticalAlignment userLocationVerticalAlignment;
+@property (nonatomic, assign) MGLAnnotationVerticalAlignment userLocationVerticalAlignment __attribute__((deprecated("Use -[MGLMapViewDelegate mapViewUserLocationAnchorPoint:] instead.")));
/**
Sets the vertical alignment of the user location annotation within the
@@ -427,7 +427,20 @@ MGL_EXPORT IB_DESIGNABLE
position within the map view. If `NO`, the user location annotation
instantaneously moves to its new position.
*/
-- (void)setUserLocationVerticalAlignment:(MGLAnnotationVerticalAlignment)alignment animated:(BOOL)animated;
+- (void)setUserLocationVerticalAlignment:(MGLAnnotationVerticalAlignment)alignment animated:(BOOL)animated __attribute__((deprecated("Use -[MGLMapViewDelegate mapViewUserLocationAnchorPoint:] instead.")));
+
+/**
+ Updates the position of the user location annotation view by retreiving the user's last
+ known location.
+ */
+- (void)updateUserLocationAnnotationView;
+
+/**
+ Updates the position of the user location annotation view by retreiving the user's last
+ known location with a specified duration.
+ @param duration The duration to animate the change in seconds.
+*/
+- (void)updateUserLocationAnnotationViewAnimatedWithDuration:(NSTimeInterval)duration;
/**
A Boolean value indicating whether the user location annotation may display a
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 12c86f1d97..8fdd393e48 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -5280,9 +5280,9 @@ public:
correctPoint.x - CGRectGetMidX(bounds),
correctPoint.y - CGRectGetMidY(bounds));
return UIEdgeInsetsMake(CGRectGetMinY(boundsAroundCorrectPoint) - CGRectGetMinY(bounds),
- self.contentInset.left,
+ CGRectGetMaxX(boundsAroundCorrectPoint) - CGRectGetMaxX(bounds),
CGRectGetMaxY(bounds) - CGRectGetMaxY(boundsAroundCorrectPoint),
- self.contentInset.right);
+ CGRectGetMaxX(bounds) - CGRectGetMaxX(boundsAroundCorrectPoint));
}
/// Returns the edge padding to apply during bifocal course tracking.
@@ -5998,15 +5998,21 @@ public:
/// the overall map view (but respecting the content inset).
- (CGPoint)userLocationAnnotationViewCenter
{
+ if ([self.delegate respondsToSelector:@selector(mapViewUserLocationAnchorPoint:)])
+ {
+ CGPoint anchorPoint = [self.delegate mapViewUserLocationAnchorPoint:self];
+ return CGPointMake(anchorPoint.x + self.contentInset.left, anchorPoint.y + self.contentInset.top);
+ }
+
CGRect contentFrame = UIEdgeInsetsInsetRect(self.contentFrame, self.edgePaddingForFollowingWithCourse);
+
if (CGRectIsEmpty(contentFrame))
{
contentFrame = self.contentFrame;
}
+
CGPoint center = CGPointMake(CGRectGetMidX(contentFrame), CGRectGetMidY(contentFrame));
-
- // When tracking course, it’s more important to see the road ahead, so
- // weight the user dot down towards the bottom.
+
switch (self.userLocationVerticalAlignment) {
case MGLAnnotationVerticalAlignmentCenter:
break;
@@ -6017,7 +6023,7 @@ public:
center.y = CGRectGetMaxY(contentFrame);
break;
}
-
+
return center;
}
diff --git a/platform/ios/src/MGLMapViewDelegate.h b/platform/ios/src/MGLMapViewDelegate.h
index 4bd1a95c9b..77dd2e4ef4 100644
--- a/platform/ios/src/MGLMapViewDelegate.h
+++ b/platform/ios/src/MGLMapViewDelegate.h
@@ -308,6 +308,21 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)mapView:(MGLMapView *)mapView didChangeUserTrackingMode:(MGLUserTrackingMode)mode animated:(BOOL)animated;
+/**
+ Returns a screen coordinate at which to position the user location annotation.
+ This coordinate is relative to the map view’s origin after applying the map view’s
+ content insets.
+
+ When unimplemented, the user location annotation is aligned within the center of
+ the map view with respect to the content insets.
+
+ This method will override any values set by `MGLMapView.userLocationVerticalAlignment`
+ or `-[MGLMapView setUserLocationVerticalAlignment:]`.
+
+ @param mapView The map view that is tracking the user's location.
+ */
+- (CGPoint)mapViewUserLocationAnchorPoint:(MGLMapView *)mapView;
+
#pragma mark Managing the Appearance of Annotations
/**
diff --git a/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift b/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift
index 48673b1d14..4904cb185b 100644
--- a/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift
+++ b/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift
@@ -92,4 +92,6 @@ extension MGLMapViewDelegateIntegrationTests: MGLMapViewDelegate {
func mapView(_ mapView: MGLMapView, shouldChangeFrom oldCamera: MGLMapCamera, to newCamera: MGLMapCamera) -> Bool { return false }
func mapView(_ mapView: MGLMapView, shouldChangeFrom oldCamera: MGLMapCamera, to newCamera: MGLMapCamera, reason: MGLCameraChangeReason) -> Bool { return false }
+
+ func mapViewUserLocationAnchorPoint(_ mapView: MGLMapView) -> CGPoint { return CGPoint(x: 100, y: 100) }
}