diff options
-rw-r--r-- | platform/ios/CHANGELOG.md | 6 | ||||
-rw-r--r-- | platform/ios/Integration Test Harness/Info.plist | 2 | ||||
-rw-r--r-- | platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m | 26 | ||||
-rw-r--r-- | platform/ios/Integration Tests/MGLMapViewIntegrationTest.h | 1 | ||||
-rw-r--r-- | platform/ios/Integration Tests/MGLMapViewIntegrationTest.m | 7 | ||||
-rw-r--r-- | platform/ios/Integration Tests/MGLTestLocationManager.h | 10 | ||||
-rw-r--r-- | platform/ios/Integration Tests/MGLTestLocationManager.m | 44 | ||||
-rw-r--r-- | platform/ios/ios.xcodeproj/project.pbxproj | 7 | ||||
-rw-r--r-- | platform/ios/src/MGLMapView.h | 17 | ||||
-rw-r--r-- | platform/ios/src/MGLMapView.mm | 18 | ||||
-rw-r--r-- | platform/ios/src/MGLMapViewDelegate.h | 15 | ||||
-rw-r--r-- | platform/ios/test/MGLMapViewDelegateIntegrationTests.swift | 2 |
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) } } |