diff options
author | Julian Rex <julian.rex@mapbox.com> | 2019-07-10 23:12:59 -0400 |
---|---|---|
committer | Julian Rex <julian.rex@mapbox.com> | 2019-08-23 14:48:17 -0400 |
commit | 77138dde3ad30a120d6afe8507bf865433956e86 (patch) | |
tree | e2060a9681fb7f74875eb0e38f386a76ff8665a3 | |
parent | 5c22f3f236c68385e09b4ea0495badd947404480 (diff) | |
download | qtlocation-mapboxgl-77138dde3ad30a120d6afe8507bf865433956e86.tar.gz |
[ios] Added "pending" completion blocks - to ensure that (for example) annotation views are in the position they are expected to be at the end of an animation.
-rw-r--r-- | platform/ios/src/MGLMapView.mm | 140 |
1 files changed, 110 insertions, 30 deletions
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index d242feb38d..8f0c451d63 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -254,6 +254,7 @@ public: @property (nonatomic) NSMutableDictionary<NSString *, NSMutableArray<MGLAnnotationView *> *> *annotationViewReuseQueueByIdentifier; @property (nonatomic, readonly) BOOL enablePresentsWithTransaction; @property (nonatomic) UIImage *lastSnapshotImage; +@property (nonatomic) NSMutableArray *pendingCompletionBlocks; /// Experimental rendering performance measurement. @property (nonatomic) BOOL experimental_enableFrameRateMeasurement; @@ -619,6 +620,11 @@ public: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; + + // Pending completion blocks are called *after* annotation views have been updated + // in updateFromDisplayLink. + _pendingCompletionBlocks = [NSMutableArray array]; + // As of 3.7.5, we intentionally do not listen for `UIApplicationWillResignActiveNotification` or call `pauseRendering:` in response to it, as doing // so causes a loop when asking for location permission. See: https://github.com/mapbox/mapbox-gl-native/issues/11225 @@ -1040,6 +1046,33 @@ public: return CGPointMake(CGRectGetMidX(contentFrame), CGRectGetMidY(contentFrame)); } +#pragma mark - Pending completion blocks + +- (void)processPendingBlocks +{ + NSArray *blocks = self.pendingCompletionBlocks; + self.pendingCompletionBlocks = [NSMutableArray array]; + + for (dispatch_block_t block in blocks) + { + block(); + } +} + +- (BOOL)addPendingBlock:(dispatch_block_t)block +{ + // Only add a block if the display link (that calls processPendingBlocks) is + // running, otherwise fall back to calling immediately. + BOOL addBlock = (_displayLink && !_displayLink.isPaused); + + if (addBlock) + { + [self.pendingCompletionBlocks addObject:block]; + } + + return addBlock; +} + #pragma mark - Life Cycle - - (void)updateFromDisplayLink:(CADisplayLink *)displayLink @@ -1064,7 +1097,7 @@ public: return; } - if (_needsDisplayRefresh) + if (_needsDisplayRefresh || (self.pendingCompletionBlocks.count > 0)) { _needsDisplayRefresh = NO; @@ -1073,6 +1106,13 @@ public: [self updateAnnotationViews]; [self updateCalloutView]; + // Call any pending completion blocks. This is primarily to ensure + // that annotations are in the expected position after core rendering + // and map update. + // + // TODO: Consider using this same mechanism for delegate callbacks. + [self processPendingBlocks]; + _mbglView->display(); } @@ -1139,6 +1179,7 @@ public: { [_displayLink invalidate]; _displayLink = nil; + [self processPendingBlocks]; } } @@ -1422,6 +1463,7 @@ public: [MGLMapboxEvents flush]; _displayLink.paused = YES; + [self processPendingBlocks]; if ( ! self.glSnapshotView) { @@ -1474,6 +1516,11 @@ public: { super.hidden = hidden; _displayLink.paused = hidden; + + if (hidden) + { + [self processPendingBlocks]; + } } - (void)tintColorDidChange @@ -3311,26 +3358,35 @@ public: animationOptions.duration.emplace(MGLDurationFromTimeInterval(duration)); animationOptions.easing.emplace(MGLUnitBezierForMediaTimingFunction(function)); } + + dispatch_block_t pendingCompletion; + if (completion) { - animationOptions.transitionFinishFn = [completion]() { + __weak __typeof__(self) weakSelf = self; + + pendingCompletion = ^{ + if (![weakSelf addPendingBlock:completion]) + { + completion(); + } + }; + + animationOptions.transitionFinishFn = [pendingCompletion]() { // Must run asynchronously after the transition is completely over. // Otherwise, a call to -setCenterCoordinate: within the completion // handler would reenter the completion handler’s caller. - dispatch_async(dispatch_get_main_queue(), ^{ - completion(); - }); + + dispatch_async(dispatch_get_main_queue(), pendingCompletion); }; } MGLMapCamera *camera = [self cameraForCameraOptions:cameraOptions]; if ([self.camera isEqualToMapCamera:camera] && UIEdgeInsetsEqualToEdgeInsets(_contentInset, insets)) { - if (completion) + if (pendingCompletion) { - [self animateWithDelay:duration animations:^{ - completion(); - }]; + [self animateWithDelay:duration animations:pendingCompletion]; } return; } @@ -3497,12 +3553,22 @@ public: animationOptions.duration.emplace(MGLDurationFromTimeInterval(duration)); animationOptions.easing.emplace(MGLUnitBezierForMediaTimingFunction(function)); } + + dispatch_block_t pendingCompletion; + if (completion) { - animationOptions.transitionFinishFn = [completion]() { - dispatch_async(dispatch_get_main_queue(), ^{ + __weak __typeof__(self) weakSelf = self; + + pendingCompletion = ^{ + if (![weakSelf addPendingBlock:completion]) + { completion(); - }); + } + }; + + animationOptions.transitionFinishFn = [pendingCompletion]() { + dispatch_async(dispatch_get_main_queue(), pendingCompletion); }; } @@ -3512,11 +3578,9 @@ public: MGLMapCamera *camera = [self cameraForCameraOptions:cameraOptions]; if ([self.camera isEqualToMapCamera:camera]) { - if (completion) + if (pendingCompletion) { - [self animateWithDelay:duration animations:^{ - completion(); - }]; + [self animateWithDelay:duration animations:pendingCompletion]; } return; } @@ -3657,22 +3721,30 @@ public: animationOptions.duration.emplace(MGLDurationFromTimeInterval(duration)); animationOptions.easing.emplace(MGLUnitBezierForMediaTimingFunction(function)); } + + dispatch_block_t pendingCompletion; + if (completion) { - animationOptions.transitionFinishFn = [completion]() { - dispatch_async(dispatch_get_main_queue(), ^{ + __weak __typeof__(self) weakSelf = self; + + pendingCompletion = ^{ + if (![weakSelf addPendingBlock:completion]) + { completion(); - }); + } + }; + + animationOptions.transitionFinishFn = [pendingCompletion]() { + dispatch_async(dispatch_get_main_queue(), pendingCompletion); }; } if ([self.camera isEqualToMapCamera:camera] && UIEdgeInsetsEqualToEdgeInsets(_contentInset, edgePadding)) { - if (completion) + if (pendingCompletion) { - [self animateWithDelay:duration animations:^{ - completion(); - }]; + [self animateWithDelay:duration animations:pendingCompletion]; } return; } @@ -3728,22 +3800,30 @@ public: animationOptions.minZoom = MGLZoomLevelForAltitude(peakAltitude, peakPitch, peakLatitude, self.frame.size); } + + dispatch_block_t pendingCompletion; + if (completion) { - animationOptions.transitionFinishFn = [completion]() { - dispatch_async(dispatch_get_main_queue(), ^{ + __weak __typeof__(self) weakSelf = self; + + pendingCompletion = ^{ + if (![weakSelf addPendingBlock:completion]) + { completion(); - }); + } + }; + + animationOptions.transitionFinishFn = [pendingCompletion]() { + dispatch_async(dispatch_get_main_queue(), pendingCompletion); }; } if ([self.camera isEqualToMapCamera:camera] && UIEdgeInsetsEqualToEdgeInsets(_contentInset, insets)) { - if (completion) + if (pendingCompletion) { - [self animateWithDelay:duration animations:^{ - completion(); - }]; + [self animateWithDelay:duration animations:pendingCompletion]; } return; } |