Skip to content
This repository was archived by the owner on Aug 8, 2023. It is now read-only.

Commit 89853dd

Browse files
author
Julian Rex
committed
[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.
1 parent cdd47c0 commit 89853dd

File tree

1 file changed

+110
-30
lines changed

1 file changed

+110
-30
lines changed

platform/ios/src/MGLMapView.mm

Lines changed: 110 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)