diff options
author | Minh Nguyễn <mxn@1ec5.org> | 2019-06-21 17:45:36 -0700 |
---|---|---|
committer | Jason Wray <friedbunny@users.noreply.github.com> | 2019-06-21 17:45:36 -0700 |
commit | f7f74a6deb57ad4ac22a30604a0dce39024feca2 (patch) | |
tree | a65905601da41fbcbf771235c6ede56389950ce2 /platform/ios | |
parent | c40b7279a2df68edb6c7b1ba11586bcbc724e21c (diff) | |
download | qtlocation-mapboxgl-f7f74a6deb57ad4ac22a30604a0dce39024feca2.tar.gz |
[ios, macos] Added completion handlers to animated MGLMapView methods
Diffstat (limited to 'platform/ios')
-rw-r--r-- | platform/ios/CHANGELOG.md | 7 | ||||
-rw-r--r-- | platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m | 9 | ||||
-rw-r--r-- | platform/ios/src/MGLMapView.h | 187 | ||||
-rw-r--r-- | platform/ios/src/MGLMapView.mm | 176 |
4 files changed, 287 insertions, 92 deletions
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 01d70385ac..ec4aa42cc3 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -5,6 +5,13 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ## 5.2.0 * Fixed an issue where the two-finger tilt gesture would continue after lifting one finger. ([#14969](https://github.com/mapbox/mapbox-gl-native/pull/14969)) +* Added variants of several animated `MGLMapView` methods that accept completion handlers ([#14381](https://github.com/mapbox/mapbox-gl-native/pull/14381)): + * `-[MGLMapView setVisibleCoordinateBounds:edgePadding:animated:completionHandler:]` + * `-[MGLMapView setContentInset:animated:completionHandler:]` + * `-[MGLMapView setUserTrackingMode:animated:completionHandler:]` + * `-[MGLMapView setTargetCoordinate:animated:completionHandler:]` + * `-[MGLMapView showAnnotations:edgePadding:animated:completionHandler:]` + * `-[MGLMapView selectAnnotation:animated:completionHandler:]` ## 5.1.0 - June 19, 2019 diff --git a/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m b/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m index 777afe3380..8b5d94f6f3 100644 --- a/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m +++ b/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m @@ -112,13 +112,16 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f }; // Also note, that at this point, the internal mechanism that determines if // an annotation view is offscreen and should be put back in the reuse queue // will have run, and `viewForAnnotation` may return nil - - [self.mapView selectAnnotation:point moveIntoView:moveIntoView animateSelection:animateSelection]; + + XCTestExpectation *selectionCompleted = [self expectationWithDescription:@"Selection completed"]; + [self.mapView selectAnnotation:point moveIntoView:moveIntoView animateSelection:animateSelection completionHandler:^{ + [selectionCompleted fulfill]; + }]; // Animated selection takes MGLAnimationDuration (0.3 seconds), so wait a little // longer. We don't need to wait as long if we're not animated (but we do // want the runloop to tick over) - [self waitFor:animateSelection ? 0.4: 0.05]; + [self waitForExpectations:@[selectionCompleted] timeout:animateSelection ? 0.4: 0.05]; UIView *annotationViewAfterSelection = [self.mapView viewForAnnotation:point]; CLLocationCoordinate2D mapCenterAfterSelection = self.mapView.centerCoordinate; diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index 3baae2ee3c..67b8033c55 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -504,6 +504,9 @@ MGL_EXPORT /** Sets the mode used to track the user location, with an optional transition. + + To specify a completion handler to execute after the animation finishes, use + the `-setUserTrackingMode:animated:completionHandler:` method. @param mode The mode used to track the user location. @param animated If `YES`, there is an animated transition from the current @@ -515,6 +518,20 @@ MGL_EXPORT - (void)setUserTrackingMode:(MGLUserTrackingMode)mode animated:(BOOL)animated; /** + Sets the mode used to track the user location, with an optional transition and + completion handler. + + @param mode The mode used to track the user location. + @param animated If `YES`, there is an animated transition from the current + viewport to a viewport that results from the change to `mode`. If `NO`, the + map view instantaneously changes to the new viewport. This parameter only + affects the initial transition; subsequent changes to the user location or + heading are always animated. + @param completion The block executed after the animation finishes. + */ +- (void)setUserTrackingMode:(MGLUserTrackingMode)mode animated:(BOOL)animated completionHandler:(nullable void (^)(void))completion; + +/** The vertical alignment of the user location annotation within the receiver. The default value is `MGLAnnotationVerticalAlignmentCenter`. @@ -604,6 +621,9 @@ MGL_EXPORT This method has no effect if the `userTrackingMode` property is set to a value other than `MGLUserTrackingModeFollowWithCourse`. + + To specify a completion handler to execute after the animation finishes, use + the `-setTargetCoordinate:animated:completionHandler:` method. @param targetCoordinate The target coordinate to fit within the viewport. @param animated If `YES`, the map animates to fit the target within the map @@ -611,6 +631,28 @@ MGL_EXPORT */ - (void)setTargetCoordinate:(CLLocationCoordinate2D)targetCoordinate animated:(BOOL)animated; +/** + Sets the geographic coordinate that is the subject of observation as the user + location is being tracked, with an optional transition animation and completion + handler. + + By default, the target coordinate is set to an invalid coordinate, indicating + that there is no target. In course tracking mode, the target forms one of two + foci in the viewport, the other being the user location annotation. Typically, + the target is set to a destination or waypoint in a real-time navigation scene. + As the user annotation moves toward the target, the map automatically zooms in + to fit both foci optimally within the viewport. + + This method has no effect if the `userTrackingMode` property is set to a value + other than `MGLUserTrackingModeFollowWithCourse`. + + @param targetCoordinate The target coordinate to fit within the viewport. + @param animated If `YES`, the map animates to fit the target within the map + view. If `NO`, the map fits the target instantaneously. + @param completion The block executed after the animation finishes. + */ +- (void)setTargetCoordinate:(CLLocationCoordinate2D)targetCoordinate animated:(BOOL)animated completionHandler:(nullable void (^)(void))completion; + #pragma mark Configuring How the User Interacts with the Map /** @@ -892,13 +934,16 @@ MGL_EXPORT - (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds animated:(BOOL)animated; /** - Changes the receiver’s viewport to fit the given coordinate bounds and - optionally some additional padding on each side. + Changes the receiver’s viewport to fit the given coordinate bounds with some + additional padding on each side. To bring both sides of the antimeridian or international date line into view, specify some longitudes less than −180 degrees or greater than 180 degrees. For example, to show both Tokyo and San Francisco simultaneously, you could set the visible bounds to extend from (35.68476, −220.24257) to (37.78428, −122.41310). + + To specify a completion handler to execute after the animation finishes, use + the `-setVisibleCoordinateBounds:edgePadding:animated:completionHandler:` method. @param bounds The bounds that the viewport will show in its entirety. @param insets The minimum padding (in screen points) that will be visible @@ -909,8 +954,26 @@ MGL_EXPORT - (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated; /** - Changes the receiver’s viewport to fit all of the given coordinates and - optionally some additional padding on each side. + Changes the receiver’s viewport to fit the given coordinate bounds with some + additional padding on each side, optionally calling a completion handler. + + To bring both sides of the antimeridian or international date line into view, + specify some longitudes less than −180 degrees or greater than 180 degrees. For + example, to show both Tokyo and San Francisco simultaneously, you could set the + visible bounds to extend from (35.68476, −220.24257) to (37.78428, −122.41310). + + @param bounds The bounds that the viewport will show in its entirety. + @param insets The minimum padding (in screen points) that will be visible + around the given coordinate bounds. + @param animated Specify `YES` to animate the change by smoothly scrolling and + zooming or `NO` to immediately display the given bounds. + @param completion The block executed after the animation finishes. + */ +- (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated completionHandler:(nullable void (^)(void))completion; + +/** + Changes the receiver’s viewport to fit all of the given coordinates with some + additional padding on each side. To bring both sides of the antimeridian or international date line into view, specify some longitudes less than −180 degrees or greater than 180 degrees. For @@ -928,8 +991,8 @@ MGL_EXPORT - (void)setVisibleCoordinates:(const CLLocationCoordinate2D *)coordinates count:(NSUInteger)count edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated; /** - Changes the receiver’s viewport to fit all of the given coordinates and - optionally some additional padding on each side. + Changes the receiver’s viewport to fit all of the given coordinates with some + additional padding on each side, optionally calling a completion handler. To bring both sides of the antimeridian or international date line into view, specify some longitudes less than −180 degrees or greater than 180 degrees. For @@ -970,6 +1033,9 @@ MGL_EXPORT Calling this method updates the value in the `visibleCoordinateBounds` property and potentially other properties to reflect the new map region. + + To specify a completion handler to execute after the animation finishes, use + the `-showAnnotations:edgePadding:animated:completionHandler:` method. @param annotations The annotations that you want to be visible in the map. @param insets The minimum padding (in screen points) around the edges of the @@ -980,6 +1046,23 @@ MGL_EXPORT - (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated; /** + Sets the visible region so that the map displays the specified annotations with + the specified amount of padding on each side and an optional completion + handler. + + Calling this method updates the value in the `visibleCoordinateBounds` property + and potentially other properties to reflect the new map region. + + @param annotations The annotations that you want to be visible in the map. + @param insets The minimum padding (in screen points) around the edges of the + map view to keep clear of annotations. + @param animated `YES` if you want the map region change to be animated, or `NO` + if you want the map to display the new region immediately without animations. + @param completion The block executed after the animation finishes. + */ +- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated completionHandler:(nullable void (^)(void))completion; + +/** A camera representing the current viewpoint of the map. */ @property (nonatomic, copy) MGLMapCamera *camera; @@ -1117,8 +1200,8 @@ MGL_EXPORT - (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds; /** - Returns the camera that best fits the given coordinate bounds, optionally with - some additional padding on each side. + Returns the camera that best fits the given coordinate bounds with some + additional padding on each side. @param bounds The coordinate bounds to fit to the receiver’s viewport. @param insets The minimum padding (in screen points) that would be visible @@ -1135,8 +1218,9 @@ MGL_EXPORT - (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets; /** - Returns the camera that best fits the given coordinate bounds, with the specified camera, - optionally with some additional padding on each side. + Returns the camera that best fits the given coordinate bounds with some + additional padding on each side, matching an existing camera as much as + possible. @param camera The camera that the return camera should adhere to. All values on this camera will be manipulated except for pitch and direction. @@ -1155,8 +1239,8 @@ MGL_EXPORT - (MGLMapCamera *)camera:(MGLMapCamera *)camera fittingCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets; /** - Returns the camera that best fits the given shape, with the specified camera, - optionally with some additional padding on each side. + Returns the camera that best fits the given shape with some additional padding + on each side, matching an existing camera as much as possible. @param camera The camera that the return camera should adhere to. All values on this camera will be manipulated except for pitch and direction. @@ -1164,8 +1248,8 @@ MGL_EXPORT @param insets The minimum padding (in screen points) that would be visible around the returned camera object if it were set as the receiver’s camera. @return A camera object centered on the shape's center with zoom level as high - (close to the ground) as possible while still including the entire shape. The - initial camera's pitch and direction will be honored. + (close to the ground) as possible while still including the entire shape. + The initial camera's pitch and direction will be honored. @note The behavior of this method is undefined if called in response to `UIApplicationWillTerminateNotification`; you may receive a `nil` return value @@ -1174,16 +1258,17 @@ MGL_EXPORT - (MGLMapCamera *)camera:(MGLMapCamera *)camera fittingShape:(MGLShape *)shape edgePadding:(UIEdgeInsets)insets; /** - Returns the camera that best fits the given shape, with the specified direction, - optionally with some additional padding on each side. + Returns the camera that best fits the given shape with some additional padding + on each side while looking in the specified direction. @param shape The shape to fit to the receiver’s viewport. - @param direction The direction of the viewport, measured in degrees clockwise from true north. + @param direction The direction of the viewport, measured in degrees clockwise + from true north. @param insets The minimum padding (in screen points) that would be visible around the returned camera object if it were set as the receiver’s camera. @return A camera object centered on the shape's center with zoom level as high - (close to the ground) as possible while still including the entire shape. The - camera object uses the current pitch. + (close to the ground) as possible while still including the entire shape. + The camera object uses the current pitch. @note The behavior of this method is undefined if called in response to `UIApplicationWillTerminateNotification`; you may receive a `nil` return value @@ -1192,7 +1277,7 @@ MGL_EXPORT - (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(CLLocationDirection)direction edgePadding:(UIEdgeInsets)insets; /** - Returns the point in this view’s coordinate system on which to "anchor" in + Returns the point in this view’s coordinate system on which to “anchor” in response to a user-initiated gesture. For example, a pinch-to-zoom gesture would anchor the map at the midpoint of @@ -1243,6 +1328,9 @@ MGL_EXPORT When the map view’s superview is an instance of `UIViewController` whose `automaticallyAdjustsScrollViewInsets` property is `YES`, the value of this property may be overridden at any time. + + To specify a completion handler to execute after the animation finishes, use + the `-setContentInset:animated:completionHandler:` method. @param contentInset The new values to inset the content by. @param animated Specify `YES` if you want the map view to animate the change to @@ -1251,6 +1339,29 @@ MGL_EXPORT */ - (void)setContentInset:(UIEdgeInsets)contentInset animated:(BOOL)animated; +/** + Sets the distance from the edges of the map view’s frame to the edges of the + map view’s logical viewport with an optional transition animation and + completion handler. + + When the value of this property is equal to `UIEdgeInsetsZero`, viewport + properties such as `centerCoordinate` assume a viewport that matches the map + view’s frame. Otherwise, those properties are inset, excluding part of the + frame from the viewport. For instance, if the only the top edge is inset, the + map center is effectively shifted downward. + + When the map view’s superview is an instance of `UIViewController` whose + `automaticallyAdjustsScrollViewInsets` property is `YES`, the value of this + property may be overridden at any time. + + @param contentInset The new values to inset the content by. + @param animated Specify `YES` if you want the map view to animate the change to + the content inset or `NO` if you want the map to inset the content + immediately. + @param completion The block executed after the animation finishes. + */ +- (void)setContentInset:(UIEdgeInsets)contentInset animated:(BOOL)animated completionHandler:(nullable void (^)(void))completion; + #pragma mark Converting Geographic Coordinates /** @@ -1501,6 +1612,9 @@ MGL_EXPORT | `YES` | The annotation is selected, and the callout is presented. If the annotation is not visible (or is partially visible) *and* is of type `MGLPointAnnotation`, the map is panned so that the annotation and its callout are brought into view. The annotation is *not* centered within the viewport. | Note that a selection initiated by a single tap gesture is always animated. + + To specify a completion handler to execute after the animation finishes, use + the `-selectAnnotation:animated:completionHandler:` method. @param annotation The annotation object to select. @param animated If `YES`, the annotation and callout view are animated on-screen. @@ -1511,15 +1625,40 @@ MGL_EXPORT - (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated; /** + Selects an annotation and displays its callout view with an optional completion + handler. + + The `animated` parameter determines whether the selection is animated including whether the map is + panned to bring the annotation into view, specifically: + + | `animated` parameter | Effect | + |------------------|--------| + | `NO` | The annotation is selected, and the callout is presented. However the map is not panned to bring the annotation or callout into view. The presentation of the callout is NOT animated. | + | `YES` | The annotation is selected, and the callout is presented. If the annotation is not visible (or is partially visible) *and* is of type `MGLPointAnnotation`, the map is panned so that the annotation and its callout are brought into view. The annotation is *not* centered within the viewport. | + + Note that a selection initiated by a single tap gesture is always animated. + + @param annotation The annotation object to select. + @param animated If `YES`, the annotation and callout view are animated on-screen. + @param completion The block executed after the animation finishes. + + @note In versions prior to `4.0.0` selecting an offscreen annotation did not + change the camera. + */ +- (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated completionHandler:(nullable void (^)(void))completion; + +/** :nodoc: - Selects an annotation and displays its callout view. This method should be - considered "alpha" and as such is liable to change. + Selects an annotation and displays its callout view with an optional completion + handler. This method should be considered "alpha" and as such is subject to + change. @param annotation The annotation object to select. - @param moveIntoView If the annotation is not visible (or is partially visible) *and* is of type `MGLPointAnnotation`, the map is panned so that the annotation and its callout are brought into view. The annotation is *not* centered within the viewport. | + @param moveIntoView If the annotation is not visible (or is partially visible) *and* is of type `MGLPointAnnotation`, the map is panned so that the annotation and its callout are brought into view. The annotation is *not* centered within the viewport. @param animateSelection If `YES`, the annotation's selection state and callout view's presentation are animated. + @param completion The block executed after the animation finishes. */ -- (void)selectAnnotation:(id <MGLAnnotation>)annotation moveIntoView:(BOOL)moveIntoView animateSelection:(BOOL)animateSelection; +- (void)selectAnnotation:(id <MGLAnnotation>)annotation moveIntoView:(BOOL)moveIntoView animateSelection:(BOOL)animateSelection completionHandler:(nullable void (^)(void))completion; /** Deselects an annotation and hides its callout view. diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 9693e3cff9..45eb345149 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1021,22 +1021,30 @@ public: - (void)setContentInset:(UIEdgeInsets)contentInset animated:(BOOL)animated { + [self setContentInset:contentInset animated:animated completionHandler:nil]; +} + +- (void)setContentInset:(UIEdgeInsets)contentInset animated:(BOOL)animated completionHandler:(nullable void (^)(void))completion +{ MGLLogDebug(@"Setting contentInset: %@ animated:", NSStringFromUIEdgeInsets(contentInset), MGLStringFromBOOL(animated)); if (UIEdgeInsetsEqualToEdgeInsets(contentInset, self.contentInset)) { + if (completion) { + completion(); + } return; } if (self.userTrackingMode == MGLUserTrackingModeNone) { // Don’t call -setCenterCoordinate:, which resets the user tracking mode. - [self _setCenterCoordinate:self.centerCoordinate edgePadding:contentInset zoomLevel:self.zoomLevel direction:self.direction duration:animated ? MGLAnimationDuration : 0 animationTimingFunction:nil completionHandler:nil]; + [self _setCenterCoordinate:self.centerCoordinate edgePadding:contentInset zoomLevel:self.zoomLevel direction:self.direction duration:animated ? MGLAnimationDuration : 0 animationTimingFunction:nil completionHandler:completion]; _contentInset = contentInset; } else { _contentInset = contentInset; - [self didUpdateLocationWithUserTrackingAnimated:animated]; + [self didUpdateLocationWithUserTrackingAnimated:animated completionHandler:completion]; } // Compass, logo and attribution button constraints needs to be updated.z @@ -1873,7 +1881,7 @@ public: { CGPoint calloutPoint = [singleTap locationInView:self]; CGRect positionRect = [self positioningRectForAnnotation:annotation defaultCalloutPoint:calloutPoint]; - [self selectAnnotation:annotation moveIntoView:YES animateSelection:YES calloutPositioningRect:positionRect]; + [self selectAnnotation:annotation moveIntoView:YES animateSelection:YES calloutPositioningRect:positionRect completionHandler:nil]; } else if (self.selectedAnnotation) { @@ -3331,28 +3339,14 @@ public: - (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated { - MGLLogDebug(@"Setting visibleCoordinateBounds: %@ edgePadding: %@ animated: %@", - MGLStringFromCoordinateBounds(bounds), - NSStringFromUIEdgeInsets(insets), - MGLStringFromBOOL(animated)); - CLLocationCoordinate2D coordinates[] = { - {bounds.ne.latitude, bounds.sw.longitude}, - bounds.sw, - {bounds.sw.latitude, bounds.ne.longitude}, - bounds.ne, - }; - [self setVisibleCoordinates:coordinates - count:sizeof(coordinates) / sizeof(coordinates[0]) - edgePadding:insets - animated:animated]; + [self setVisibleCoordinateBounds:bounds edgePadding:insets animated:animated completionHandler:nil]; } -- (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets direction:(CLLocationDirection)direction animated:(BOOL)animated +- (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated completionHandler:(nullable void (^)(void))completion { - MGLLogDebug(@"Setting visibleCoordinateBounds: %@ edgePadding: %@ direction: %f animated: %@", + MGLLogDebug(@"Setting visibleCoordinateBounds: %@ edgePadding: %@ animated: %@", MGLStringFromCoordinateBounds(bounds), NSStringFromUIEdgeInsets(insets), - direction, MGLStringFromBOOL(animated)); CLLocationCoordinate2D coordinates[] = { {bounds.ne.latitude, bounds.sw.longitude}, @@ -3363,8 +3357,10 @@ public: [self setVisibleCoordinates:coordinates count:sizeof(coordinates) / sizeof(coordinates[0]) edgePadding:insets - direction:direction - animated:animated]; + direction:self.direction + duration:animated ? MGLAnimationDuration : 0 + animationTimingFunction:nil + completionHandler:completion]; } - (void)setVisibleCoordinates:(const CLLocationCoordinate2D *)coordinates count:(NSUInteger)count edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated @@ -3373,17 +3369,7 @@ public: count, NSStringFromUIEdgeInsets(insets), MGLStringFromBOOL(animated)); - [self setVisibleCoordinates:coordinates count:count edgePadding:insets direction:self.direction animated:animated]; -} - -- (void)setVisibleCoordinates:(const CLLocationCoordinate2D *)coordinates count:(NSUInteger)count edgePadding:(UIEdgeInsets)insets direction:(CLLocationDirection)direction animated:(BOOL)animated -{ - MGLLogDebug(@"Setting: %lu coordinates edgePadding: %@ direction: %f animated: %@", - count, - NSStringFromUIEdgeInsets(insets), - direction, - MGLStringFromBOOL(animated)); - [self setVisibleCoordinates:coordinates count:count edgePadding:insets direction:direction duration:animated ? MGLAnimationDuration : 0 animationTimingFunction:nil]; + [self setVisibleCoordinates:coordinates count:count edgePadding:insets direction:self.direction duration:animated ? MGLAnimationDuration : 0 animationTimingFunction:nil]; } - (void)setVisibleCoordinates:(const CLLocationCoordinate2D *)coordinates count:(NSUInteger)count edgePadding:(UIEdgeInsets)insets direction:(CLLocationDirection)direction duration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function { @@ -4699,17 +4685,22 @@ public: - (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated { - [self selectAnnotation:annotation moveIntoView:animated animateSelection:animated]; + [self selectAnnotation:annotation animated:animated completionHandler:nil]; } -- (void)selectAnnotation:(id <MGLAnnotation>)annotation moveIntoView:(BOOL)moveIntoView animateSelection:(BOOL)animateSelection +- (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated completionHandler:(nullable void (^)(void))completion +{ + [self selectAnnotation:annotation moveIntoView:animated animateSelection:animated completionHandler:completion]; +} + +- (void)selectAnnotation:(id <MGLAnnotation>)annotation moveIntoView:(BOOL)moveIntoView animateSelection:(BOOL)animateSelection completionHandler:(nullable void (^)(void))completion { MGLLogDebug(@"Selecting annotation: %@ moveIntoView: %@ animateSelection: %@", annotation, MGLStringFromBOOL(moveIntoView), MGLStringFromBOOL(animateSelection)); CGRect positioningRect = [self positioningRectForAnnotation:annotation defaultCalloutPoint:CGPointZero]; - [self selectAnnotation:annotation moveIntoView:moveIntoView animateSelection:animateSelection calloutPositioningRect:positioningRect]; + [self selectAnnotation:annotation moveIntoView:moveIntoView animateSelection:animateSelection calloutPositioningRect:positioningRect completionHandler:completion]; } -- (void)selectAnnotation:(id <MGLAnnotation>)annotation moveIntoView:(BOOL)moveIntoView animateSelection:(BOOL)animateSelection calloutPositioningRect:(CGRect)calloutPositioningRect +- (void)selectAnnotation:(id <MGLAnnotation>)annotation moveIntoView:(BOOL)moveIntoView animateSelection:(BOOL)animateSelection calloutPositioningRect:(CGRect)calloutPositioningRect completionHandler:(nullable void (^)(void))completion { if ( ! annotation) return; @@ -4919,7 +4910,11 @@ public: { CGPoint center = CGPointMake(CGRectGetMidX(constrainedRect), CGRectGetMidY(constrainedRect)); CLLocationCoordinate2D centerCoord = [self convertPoint:center toCoordinateFromView:self]; - [self setCenterCoordinate:centerCoord animated:animateSelection]; + [self setCenterCoordinate:centerCoord zoomLevel:self.zoomLevel direction:self.direction animated:animateSelection completionHandler:completion]; + } + else if (completion) + { + completion(); } } @@ -5117,8 +5112,19 @@ public: - (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated { + [self showAnnotations:annotations edgePadding:insets animated:animated completionHandler:nil]; +} + +- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated completionHandler:(nullable void (^)(void))completion +{ MGLLogDebug(@"Showing: %lu annotations edgePadding: %@ animated: %@", annotations.count, NSStringFromUIEdgeInsets(insets), MGLStringFromBOOL(animated)); - if ( ! annotations || ! annotations.count) return; + if ( ! annotations.count) + { + if (completion) { + completion(); + } + return; + } mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty(); @@ -5136,7 +5142,8 @@ public: [self setVisibleCoordinateBounds:MGLCoordinateBoundsFromLatLngBounds(bounds) edgePadding:insets - animated:animated]; + animated:animated + completionHandler:completion]; } @@ -5345,8 +5352,20 @@ public: - (void)setUserTrackingMode:(MGLUserTrackingMode)mode animated:(BOOL)animated { + [self setUserTrackingMode:mode animated:animated completionHandler:nil]; +} + +- (void)setUserTrackingMode:(MGLUserTrackingMode)mode animated:(BOOL)animated completionHandler:(nullable void (^)(void))completion +{ MGLLogDebug(@"Setting userTrackingMode: %lu animated: %@", mode, MGLStringFromBOOL(animated)); - if (mode == _userTrackingMode) return; + if (mode == _userTrackingMode) + { + if (completion) + { + completion(); + } + return; + } MGLUserTrackingMode oldMode = _userTrackingMode; [self willChangeValueForKey:@"userTrackingMode"]; @@ -5391,13 +5410,14 @@ public: } } - if (_userTrackingMode != MGLUserTrackingModeNone) + CLLocation *location; + if (_userTrackingMode != MGLUserTrackingModeNone && (location = self.userLocation.location) && self.userLocationAnnotationView) { - CLLocation *location = self.userLocation.location; - if (location && self.userLocationAnnotationView) - { - [self locationManager:self.locationManager didUpdateLocations:@[location] animated:animated]; - } + [self locationManager:self.locationManager didUpdateLocations:@[location] animated:animated completionHandler:completion]; + } + else if (completion) + { + completion(); } [self validateUserHeadingUpdating]; @@ -5421,7 +5441,7 @@ public: CLLocation *location = self.userLocation.location; if (location) { - [self locationManager:self.locationManager didUpdateLocations:@[location] animated:animated]; + [self locationManager:self.locationManager didUpdateLocations:@[location] animated:animated completionHandler:nil]; } } } @@ -5434,7 +5454,13 @@ public: - (void)setTargetCoordinate:(CLLocationCoordinate2D)targetCoordinate animated:(BOOL)animated { + [self setTargetCoordinate:targetCoordinate animated:animated completionHandler:nil]; +} + +- (void)setTargetCoordinate:(CLLocationCoordinate2D)targetCoordinate animated:(BOOL)animated completionHandler:(nullable void (^)(void))completion +{ MGLLogDebug(@"Setting targetCoordinate: %@ animated: %@", MGLStringFromCLLocationCoordinate2D(targetCoordinate), MGLStringFromBOOL(animated)); + BOOL isSynchronous = YES; if (targetCoordinate.latitude != self.targetCoordinate.latitude || targetCoordinate.longitude != self.targetCoordinate.longitude) { @@ -5443,13 +5469,17 @@ public: { self.userTrackingState = MGLUserTrackingStatePossible; - CLLocation *location = self.userLocation.location; - if (location) + if (CLLocation *location = self.userLocation.location) { - [self locationManager:self.locationManager didUpdateLocations:@[location] animated:animated]; + isSynchronous = NO; + [self locationManager:self.locationManager didUpdateLocations:@[location] animated:animated completionHandler:completion]; } } } + if (isSynchronous && completion) + { + completion(); + } } - (void)setShowsUserHeadingIndicator:(BOOL)showsUserHeadingIndicator @@ -5481,10 +5511,10 @@ public: - (void)locationManager:(id<MGLLocationManager>)manager didUpdateLocations:(NSArray *)locations { - [self locationManager:manager didUpdateLocations:locations animated:YES]; + [self locationManager:manager didUpdateLocations:locations animated:YES completionHandler:nil]; } -- (void)locationManager:(__unused id<MGLLocationManager>)manager didUpdateLocations:(NSArray *)locations animated:(BOOL)animated +- (void)locationManager:(__unused id<MGLLocationManager>)manager didUpdateLocations:(NSArray *)locations animated:(BOOL)animated completionHandler:(nullable void (^)(void))completion { CLLocation *oldLocation = self.userLocation.location; CLLocation *newLocation = locations.lastObject; @@ -5506,7 +5536,7 @@ public: } } - [self didUpdateLocationWithUserTrackingAnimated:animated]; + [self didUpdateLocationWithUserTrackingAnimated:animated completionHandler:completion]; NSTimeInterval duration = MGLAnimationDuration; if (oldLocation && ! CGPointEqualToPoint(self.userLocationAnnotationView.center, CGPointZero)) @@ -5523,13 +5553,17 @@ public: } } -- (void)didUpdateLocationWithUserTrackingAnimated:(BOOL)animated +- (void)didUpdateLocationWithUserTrackingAnimated:(BOOL)animated completionHandler:(nullable void (^)(void))completion { CLLocation *location = self.userLocation.location; if ( ! _showsUserLocation || ! location || ! CLLocationCoordinate2DIsValid(location.coordinate) || self.userTrackingMode == MGLUserTrackingModeNone) { + if (completion) + { + completion(); + } return; } @@ -5540,6 +5574,10 @@ public: if (std::abs(currentPoint.x - correctPoint.x) <= 1.0 && std::abs(currentPoint.y - correctPoint.y) <= 1.0 && self.userTrackingMode != MGLUserTrackingModeFollowWithCourse) { + if (completion) + { + completion(); + } return; } @@ -5549,25 +5587,25 @@ public: if (self.userTrackingState != MGLUserTrackingStateBegan) { // Keep both the user and the destination in view. - [self didUpdateLocationWithTargetAnimated:animated]; + [self didUpdateLocationWithTargetAnimated:animated completionHandler:completion]; } } else if (self.userTrackingState == MGLUserTrackingStatePossible) { // The first location update is often a great distance away from the // current viewport, so fly there to provide additional context. - [self didUpdateLocationSignificantlyAnimated:animated]; + [self didUpdateLocationSignificantlyAnimated:animated completionHandler:completion]; } else if (self.userTrackingState == MGLUserTrackingStateChanged) { // Subsequent updates get a more subtle animation. - [self didUpdateLocationIncrementallyAnimated:animated]; + [self didUpdateLocationIncrementallyAnimated:animated completionHandler:completion]; } [self unrotateIfNeededAnimated:YES]; } /// Changes the viewport based on an incremental location update. -- (void)didUpdateLocationIncrementallyAnimated:(BOOL)animated +- (void)didUpdateLocationIncrementallyAnimated:(BOOL)animated completionHandler:(nullable void (^)(void))completion { [self _setCenterCoordinate:self.userLocation.location.coordinate edgePadding:self.edgePaddingForFollowing @@ -5575,12 +5613,12 @@ public: direction:self.directionByFollowingWithCourse duration:animated ? MGLUserLocationAnimationDuration : 0 animationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear] - completionHandler:nil]; + completionHandler:completion]; } /// Changes the viewport based on a significant location update, such as the /// first location update. -- (void)didUpdateLocationSignificantlyAnimated:(BOOL)animated +- (void)didUpdateLocationSignificantlyAnimated:(BOOL)animated completionHandler:(nullable void (^)(void))completion { if (_distanceFromOldUserLocation >= MGLDistanceThresholdForCameraPause) { @@ -5612,25 +5650,33 @@ public: { strongSelf.userTrackingState = MGLUserTrackingStateChanged; } + if (completion) + { + completion(); + } }]; } /// Changes the viewport based on a location update in the presence of a target /// coordinate that must also be displayed on the map concurrently. -- (void)didUpdateLocationWithTargetAnimated:(BOOL)animated +- (void)didUpdateLocationWithTargetAnimated:(BOOL)animated completionHandler:(nullable void (^)(void))completion { BOOL firstUpdate = self.userTrackingState == MGLUserTrackingStatePossible; - void (^completion)(void); + void (^animationCompletion)(void); if (animated && firstUpdate) { self.userTrackingState = MGLUserTrackingStateBegan; __weak MGLMapView *weakSelf = self; - completion = ^{ + animationCompletion = ^{ MGLMapView *strongSelf = weakSelf; if (strongSelf.userTrackingState == MGLUserTrackingStateBegan) { strongSelf.userTrackingState = MGLUserTrackingStateChanged; } + if (completion) + { + completion(); + } }; } @@ -5654,7 +5700,7 @@ public: direction:self.directionByFollowingWithCourse duration:animated ? MGLUserLocationAnimationDuration : 0 animationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear] - completionHandler:completion]; + completionHandler:animationCompletion]; } /// Returns the edge padding to apply when moving the map to a tracked location. |