diff options
author | Nadia Barbosa <nadiabarbosa@me.com> | 2018-09-17 15:45:22 -0700 |
---|---|---|
committer | Nadia Barbosa <captainbarbosa@users.noreply.github.com> | 2018-10-02 18:28:53 -0400 |
commit | 7b243392e366907b35f819ac2d416475287d74ea (patch) | |
tree | 14d6ab0d322a9d33c51c903cbf0d1e90ac33ccd3 | |
parent | 41dd886ce5e0b20657d7b859b775949055129906 (diff) |
[ios] Add delegate method to specify the user location annotation’s position
Update method name
More API drafting
Add deprecation flag
Add Swift delegate integration test
Update method name and documentation
Update deprecation notices
Update method name
Offset anchor point relative to contentFrame
Update docs
Only run through switch statement if delegate is unimplemented
Account for content inset + refactor logic
Adjust edgePaddingForFollowing
Fix Swift delegate integration test
Set up integration test
Set up test location manager
.
Remove unused file reference from test
Return CGPoint value from delegate method within integration test setup
Test anchor points
Make updateUserLocationAnnotationView public
Refactor test
Update test location manager
Changelog entry
Doc fixes
-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 9611ec9ae..7693dca33 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 4222ac2dd..be30bb5cc 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 ba63a9eff..fefb93877 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 f513df8b2..2f459a5f4 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 c427a7842..7153257df 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 000000000..e0e6f25bb --- /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 000000000..f9a5a8650 --- /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 bacfafa20..22c416d92 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 02d146edc..5312804cd 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 12c86f1d9..8fdd393e4 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 4bd1a95c9..77dd2e4ef 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 48673b1d1..4904cb185 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) } } |