@@ -247,6 +247,7 @@ @interface MGLMapView () <UIGestureRecognizerDelegate,
247247@property (nonatomic) NSMutableDictionary<NSString *, NSMutableArray<MGLAnnotationView *> *> *annotationViewReuseQueueByIdentifier;
248248@property (nonatomic, readonly) BOOL enablePresentsWithTransaction;
249249@property (nonatomic) UIImage *lastSnapshotImage;
250+ @property (nonatomic) NSMutableArray *pendingCompletionBlocks;
250251
251252/// Experimental rendering performance measurement.
252253@property (nonatomic) BOOL experimental_enableFrameRateMeasurement;
@@ -612,6 +613,11 @@ - (void)commonInit
612613 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
613614 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
614615 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
616+
617+ // Pending completion blocks are called *after* annotation views have been updated
618+ // in updateFromDisplayLink.
619+ _pendingCompletionBlocks = [NSMutableArray array];
620+
615621
616622 // As of 3.7.5, we intentionally do not listen for `UIApplicationWillResignActiveNotification` or call `pauseRendering:` in response to it, as doing
617623 // so causes a loop when asking for location permission. See: https://github.com/mapbox/mapbox-gl-native/issues/11225
@@ -1033,6 +1039,33 @@ - (CGPoint)contentCenter
10331039 return CGPointMake(CGRectGetMidX(contentFrame), CGRectGetMidY(contentFrame));
10341040}
10351041
1042+ #pragma mark - Pending completion blocks
1043+
1044+ - (void)processPendingBlocks
1045+ {
1046+ NSArray *blocks = self.pendingCompletionBlocks;
1047+ self.pendingCompletionBlocks = [NSMutableArray array];
1048+
1049+ for (dispatch_block_t block in blocks)
1050+ {
1051+ block();
1052+ }
1053+ }
1054+
1055+ - (BOOL)addPendingBlock:(dispatch_block_t)block
1056+ {
1057+ // Only add a block if the display link (that calls processPendingBlocks) is
1058+ // running, otherwise fall back to calling immediately.
1059+ BOOL addBlock = (_displayLink && !_displayLink.isPaused);
1060+
1061+ if (addBlock)
1062+ {
1063+ [self.pendingCompletionBlocks addObject:block];
1064+ }
1065+
1066+ return addBlock;
1067+ }
1068+
10361069#pragma mark - Life Cycle -
10371070
10381071- (void)updateFromDisplayLink:(CADisplayLink *)displayLink
@@ -1057,7 +1090,7 @@ - (void)updateFromDisplayLink:(CADisplayLink *)displayLink
10571090 return;
10581091 }
10591092
1060- if (_needsDisplayRefresh)
1093+ if (_needsDisplayRefresh || (self.pendingCompletionBlocks.count > 0) )
10611094 {
10621095 _needsDisplayRefresh = NO;
10631096
@@ -1066,6 +1099,13 @@ - (void)updateFromDisplayLink:(CADisplayLink *)displayLink
10661099 [self updateAnnotationViews];
10671100 [self updateCalloutView];
10681101
1102+ // Call any pending completion blocks. This is primarily to ensure
1103+ // that annotations are in the expected position after core rendering
1104+ // and map update.
1105+ //
1106+ // TODO: Consider using this same mechanism for delegate callbacks.
1107+ [self processPendingBlocks];
1108+
10691109 _mbglView->display();
10701110 }
10711111
@@ -1132,6 +1172,7 @@ - (void)validateDisplayLink
11321172 {
11331173 [_displayLink invalidate];
11341174 _displayLink = nil;
1175+ [self processPendingBlocks];
11351176 }
11361177}
11371178
@@ -1415,6 +1456,7 @@ - (void)pauseRendering:(__unused NSNotification *)notification
14151456 [MGLMapboxEvents flush];
14161457
14171458 _displayLink.paused = YES;
1459+ [self processPendingBlocks];
14181460
14191461 if ( ! self.glSnapshotView)
14201462 {
@@ -1467,6 +1509,11 @@ - (void)setHidden:(BOOL)hidden
14671509{
14681510 super.hidden = hidden;
14691511 _displayLink.paused = hidden;
1512+
1513+ if (hidden)
1514+ {
1515+ [self processPendingBlocks];
1516+ }
14701517}
14711518
14721519- (void)tintColorDidChange
@@ -3188,26 +3235,35 @@ - (void)_setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate edgePaddin
31883235 animationOptions.duration.emplace(MGLDurationFromTimeInterval(duration));
31893236 animationOptions.easing.emplace(MGLUnitBezierForMediaTimingFunction(function));
31903237 }
3238+
3239+ dispatch_block_t pendingCompletion;
3240+
31913241 if (completion)
31923242 {
3193- animationOptions.transitionFinishFn = [completion]() {
3243+ __weak __typeof__(self) weakSelf = self;
3244+
3245+ pendingCompletion = ^{
3246+ if (![weakSelf addPendingBlock:completion])
3247+ {
3248+ completion();
3249+ }
3250+ };
3251+
3252+ animationOptions.transitionFinishFn = [pendingCompletion]() {
31943253 // Must run asynchronously after the transition is completely over.
31953254 // Otherwise, a call to -setCenterCoordinate: within the completion
31963255 // handler would reenter the completion handler’s caller.
3197- dispatch_async(dispatch_get_main_queue(), ^{
3198- completion();
3199- });
3256+
3257+ dispatch_async(dispatch_get_main_queue(), pendingCompletion);
32003258 };
32013259 }
32023260
32033261 MGLMapCamera *camera = [self cameraForCameraOptions:cameraOptions];
32043262 if ([self.camera isEqualToMapCamera:camera] && UIEdgeInsetsEqualToEdgeInsets(_contentInset, insets))
32053263 {
3206- if (completion )
3264+ if (pendingCompletion )
32073265 {
3208- [self animateWithDelay:duration animations:^{
3209- completion();
3210- }];
3266+ [self animateWithDelay:duration animations:pendingCompletion];
32113267 }
32123268 return;
32133269 }
@@ -3374,12 +3430,22 @@ - (void)_setVisibleCoordinates:(const CLLocationCoordinate2D *)coordinates count
33743430 animationOptions.duration.emplace(MGLDurationFromTimeInterval(duration));
33753431 animationOptions.easing.emplace(MGLUnitBezierForMediaTimingFunction(function));
33763432 }
3433+
3434+ dispatch_block_t pendingCompletion;
3435+
33773436 if (completion)
33783437 {
3379- animationOptions.transitionFinishFn = [completion]() {
3380- dispatch_async(dispatch_get_main_queue(), ^{
3438+ __weak __typeof__(self) weakSelf = self;
3439+
3440+ pendingCompletion = ^{
3441+ if (![weakSelf addPendingBlock:completion])
3442+ {
33813443 completion();
3382- });
3444+ }
3445+ };
3446+
3447+ animationOptions.transitionFinishFn = [pendingCompletion]() {
3448+ dispatch_async(dispatch_get_main_queue(), pendingCompletion);
33833449 };
33843450 }
33853451
@@ -3389,11 +3455,9 @@ - (void)_setVisibleCoordinates:(const CLLocationCoordinate2D *)coordinates count
33893455 MGLMapCamera *camera = [self cameraForCameraOptions:cameraOptions];
33903456 if ([self.camera isEqualToMapCamera:camera])
33913457 {
3392- if (completion )
3458+ if (pendingCompletion )
33933459 {
3394- [self animateWithDelay:duration animations:^{
3395- completion();
3396- }];
3460+ [self animateWithDelay:duration animations:pendingCompletion];
33973461 }
33983462 return;
33993463 }
@@ -3534,22 +3598,30 @@ - (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration a
35343598 animationOptions.duration.emplace(MGLDurationFromTimeInterval(duration));
35353599 animationOptions.easing.emplace(MGLUnitBezierForMediaTimingFunction(function));
35363600 }
3601+
3602+ dispatch_block_t pendingCompletion;
3603+
35373604 if (completion)
35383605 {
3539- animationOptions.transitionFinishFn = [completion]() {
3540- dispatch_async(dispatch_get_main_queue(), ^{
3606+ __weak __typeof__(self) weakSelf = self;
3607+
3608+ pendingCompletion = ^{
3609+ if (![weakSelf addPendingBlock:completion])
3610+ {
35413611 completion();
3542- });
3612+ }
3613+ };
3614+
3615+ animationOptions.transitionFinishFn = [pendingCompletion]() {
3616+ dispatch_async(dispatch_get_main_queue(), pendingCompletion);
35433617 };
35443618 }
35453619
35463620 if ([self.camera isEqualToMapCamera:camera] && UIEdgeInsetsEqualToEdgeInsets(_contentInset, edgePadding))
35473621 {
3548- if (completion )
3622+ if (pendingCompletion )
35493623 {
3550- [self animateWithDelay:duration animations:^{
3551- completion();
3552- }];
3624+ [self animateWithDelay:duration animations:pendingCompletion];
35533625 }
35543626 return;
35553627 }
@@ -3605,22 +3677,30 @@ - (void)_flyToCamera:(MGLMapCamera *)camera edgePadding:(UIEdgeInsets)insets wit
36053677 animationOptions.minZoom = MGLZoomLevelForAltitude(peakAltitude, peakPitch,
36063678 peakLatitude, self.frame.size);
36073679 }
3680+
3681+ dispatch_block_t pendingCompletion;
3682+
36083683 if (completion)
36093684 {
3610- animationOptions.transitionFinishFn = [completion]() {
3611- dispatch_async(dispatch_get_main_queue(), ^{
3685+ __weak __typeof__(self) weakSelf = self;
3686+
3687+ pendingCompletion = ^{
3688+ if (![weakSelf addPendingBlock:completion])
3689+ {
36123690 completion();
3613- });
3691+ }
3692+ };
3693+
3694+ animationOptions.transitionFinishFn = [pendingCompletion]() {
3695+ dispatch_async(dispatch_get_main_queue(), pendingCompletion);
36143696 };
36153697 }
36163698
36173699 if ([self.camera isEqualToMapCamera:camera] && UIEdgeInsetsEqualToEdgeInsets(_contentInset, insets))
36183700 {
3619- if (completion )
3701+ if (pendingCompletion )
36203702 {
3621- [self animateWithDelay:duration animations:^{
3622- completion();
3623- }];
3703+ [self animateWithDelay:duration animations:pendingCompletion];
36243704 }
36253705 return;
36263706 }
0 commit comments