[ios] Added pending completion blocks (#15122)

This commit is contained in:
Julian Rex
2019-08-25 01:02:29 -04:00
committed by GitHub
parent 5c066f874c
commit 4010b1d256
4 changed files with 484 additions and 31 deletions

View File

@@ -5,6 +5,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
## master
* Fixed an issue that caused the tilt gesture to trigger too easily and conflict with pinch or pan gestures. ([#15349](https://github.com/mapbox/mapbox-gl-native/pull/15349))
* Fixed a bug with annotation view positions after camera transitions. ([#15122](https://github.com/mapbox/mapbox-gl-native/pull/15122/))
## 5.3.0

View File

@@ -0,0 +1,367 @@
#import "MGLMapViewIntegrationTest.h"
#import "MGLTestUtility.h"
@interface MGLMapView (MGLMapViewPendingBlockTests)
@property (nonatomic) NSMutableArray *pendingCompletionBlocks;
- (void)pauseRendering:(__unused NSNotification *)notification;
@end
@interface MGLMapViewPendingBlockTests : MGLMapViewIntegrationTest
@property (nonatomic, copy) void (^observation)(NSDictionary*);
@property (nonatomic) BOOL completionHandlerCalled;
@end
@implementation MGLMapViewPendingBlockTests
- (void)testSetCenterCoordinate {
__typeof__(self) weakSelf = self;
void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0)
zoomLevel:10.0
direction:0
animated:NO
completionHandler:completion];
}
else {
completion();
}
};
[self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
transition:transition
addToPendingCallback:nil];
}
- (void)testSetCenterCoordinateAnimated {
__typeof__(self) weakSelf = self;
void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0)
zoomLevel:10.0
direction:0
animated:YES
completionHandler:completion];
}
else {
completion();
}
};
[self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
transition:transition
addToPendingCallback:nil];
}
- (void)testSetVisibleCoordinateBounds {
__typeof__(self) weakSelf = self;
void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf) {
MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
[strongSelf.mapView setVisibleCoordinateBounds:unitBounds
edgePadding:UIEdgeInsetsZero
animated:NO
completionHandler:completion];
}
else {
completion();
}
};
[self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
transition:transition
addToPendingCallback:nil];
}
- (void)testSetVisibleCoordinateBoundsAnimated {
__typeof__(self) weakSelf = self;
void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf) {
MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
[strongSelf.mapView setVisibleCoordinateBounds:unitBounds
edgePadding:UIEdgeInsetsZero
animated:YES
completionHandler:completion];
}
else {
completion();
}
};
[self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
transition:transition
addToPendingCallback:nil];
}
- (void)testSetCamera {
__typeof__(self) weakSelf = self;
void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf) {
MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
MGLMapCamera *camera = [strongSelf.mapView cameraThatFitsCoordinateBounds:unitBounds];
[strongSelf.mapView setCamera:camera withDuration:0.0 animationTimingFunction:nil completionHandler:completion];
}
else {
completion();
}
};
[self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
transition:transition
addToPendingCallback:nil];
}
- (void)testSetCameraAnimated {
__typeof__(self) weakSelf = self;
void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf) {
MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
MGLMapCamera *camera = [strongSelf.mapView cameraThatFitsCoordinateBounds:unitBounds];
[strongSelf.mapView setCamera:camera withDuration:0.3 animationTimingFunction:nil completionHandler:completion];
}
else {
completion();
}
};
[self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
transition:transition
addToPendingCallback:nil];
}
// Marked as pending due to https://github.com/mapbox/mapbox-gl-native/issues/15471
- (void)testFlyToCameraPENDING {
__typeof__(self) weakSelf = self;
void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf) {
MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
MGLMapCamera *camera = [strongSelf.mapView cameraThatFitsCoordinateBounds:unitBounds];
[strongSelf.mapView flyToCamera:camera withDuration:0.0 completionHandler:completion];
}
else {
completion();
}
};
[self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
transition:transition
addToPendingCallback:nil];
}
- (void)testFlyToCameraAnimated {
__typeof__(self) weakSelf = self;
void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf) {
MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
MGLMapCamera *camera = [strongSelf.mapView cameraThatFitsCoordinateBounds:unitBounds];
[strongSelf.mapView flyToCamera:camera withDuration:0.3 completionHandler:completion];
}
else {
completion();
}
};
[self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
transition:transition
addToPendingCallback:nil];
}
#pragma mark - test interrupting regular rendering
- (void)testSetCenterCoordinateSetHidden {
__typeof__(self) weakSelf = self;
void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0)
zoomLevel:10.0
direction:0
animated:NO
completionHandler:completion];
}
else {
completion();
}
};
dispatch_block_t addedToPending = ^{
__typeof__(self) strongSelf = weakSelf;
MGLTestAssert(strongSelf, !strongSelf.completionHandlerCalled);
// Now hide the mapview
strongSelf.mapView.hidden = YES;
MGLTestAssert(strongSelf, strongSelf.completionHandlerCalled);
};
[self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
transition:transition
addToPendingCallback:addedToPending];
}
- (void)testSetCenterCoordinatePauseRendering {
__typeof__(self) weakSelf = self;
void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0)
zoomLevel:10.0
direction:0
animated:NO
completionHandler:completion];
}
else {
completion();
}
};
dispatch_block_t addedToPending = ^{
__typeof__(self) strongSelf = weakSelf;
MGLTestAssert(strongSelf, !strongSelf.completionHandlerCalled);
// Pause rendering, stopping display link
[strongSelf.mapView pauseRendering:nil];
MGLTestAssert(strongSelf, strongSelf.completionHandlerCalled);
};
[self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
transition:transition
addToPendingCallback:addedToPending];
}
- (void)testSetCenterCoordinateRemoveFromSuperview {
__typeof__(self) weakSelf = self;
void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0)
zoomLevel:10.0
direction:0
animated:NO
completionHandler:completion];
}
else {
completion();
}
};
dispatch_block_t addedToPending = ^{
__typeof__(self) strongSelf = weakSelf;
MGLTestAssert(strongSelf, !strongSelf.completionHandlerCalled);
// Remove from window, triggering validateDisplayLink
[strongSelf.mapView removeFromSuperview];
MGLTestAssert(strongSelf, strongSelf.completionHandlerCalled);
};
[self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
transition:transition
addToPendingCallback:addedToPending];
}
#pragma mark - Shared utility methods
- (void)internalTestCompletionBlockAddedToPendingForTestName:(NSString *)testName
transition:(void (^)(dispatch_block_t))transition
addToPendingCallback:(dispatch_block_t)addToPendingCallback {
XCTestExpectation *expectation = [self expectationWithDescription:testName];
__weak __typeof__(self) myself = self;
dispatch_block_t block = ^{
myself.completionHandlerCalled = YES;
[expectation fulfill];
};
XCTAssertNotNil(transition);
transition(block);
XCTAssert(!self.completionHandlerCalled);
XCTAssert(self.mapView.pendingCompletionBlocks.count == 0);
__block BOOL blockAddedToPendingBlocks = NO;
// Observes changes to pendingCompletionBlocks (including additions)
self.observation = ^(NSDictionary *change){
NSLog(@"change = %@ count = %lu", change, myself.mapView.pendingCompletionBlocks.count);
NSArray *value = change[NSKeyValueChangeNewKey];
MGLTestAssert(myself, [value isKindOfClass:[NSArray class]]);
if (value.count > 0) {
MGLTestAssert(myself, [value containsObject:block]);
MGLTestAssert(myself, !blockAddedToPendingBlocks);
if ([myself.mapView.pendingCompletionBlocks containsObject:block]) {
blockAddedToPendingBlocks = YES;
if (addToPendingCallback) {
addToPendingCallback();
}
}
}
};
[self.mapView addObserver:self forKeyPath:@"pendingCompletionBlocks" options:NSKeyValueObservingOptionNew context:_cmd];
[self waitForExpectations:@[expectation] timeout:0.5];
XCTAssert(blockAddedToPendingBlocks);
XCTAssert(self.completionHandlerCalled);
XCTAssert(self.mapView.pendingCompletionBlocks.count == 0);
[self.mapView removeObserver:self forKeyPath:@"pendingCompletionBlocks" context:_cmd];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if (self.observation) {
self.observation(change);
}
}
@end

View File

@@ -508,6 +508,7 @@
CA1B4A512099FB2200EDD491 /* MGLMapSnapshotterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = CA1B4A502099FB2200EDD491 /* MGLMapSnapshotterTest.m */; };
CA34C9C3207FD272005C1A06 /* MGLCameraTransitionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CA34C9C2207FD272005C1A06 /* MGLCameraTransitionTests.mm */; };
CA4EB8C720863487006AB465 /* MGLStyleLayerIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA4EB8C620863487006AB465 /* MGLStyleLayerIntegrationTests.m */; };
CA4F3BDE230F74C3008BAFEA /* MGLMapViewPendingBlockTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA4F3BDD230F74C3008BAFEA /* MGLMapViewPendingBlockTests.m */; };
CA55CD41202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */; settings = {ATTRIBUTES = (Public, ); }; };
CA55CD42202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */; settings = {ATTRIBUTES = (Public, ); }; };
CA65C4F821E9BB080068B0D4 /* MGLCluster.h in Headers */ = {isa = PBXBuildFile; fileRef = CA65C4F721E9BB080068B0D4 /* MGLCluster.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -1198,11 +1199,12 @@
CA1B4A502099FB2200EDD491 /* MGLMapSnapshotterTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapSnapshotterTest.m; sourceTree = "<group>"; };
CA34C9C2207FD272005C1A06 /* MGLCameraTransitionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLCameraTransitionTests.mm; sourceTree = "<group>"; };
CA4EB8C620863487006AB465 /* MGLStyleLayerIntegrationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLStyleLayerIntegrationTests.m; sourceTree = "<group>"; };
CA4F3BDD230F74C3008BAFEA /* MGLMapViewPendingBlockTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewPendingBlockTests.m; sourceTree = "<group>"; };
CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCameraChangeReason.h; sourceTree = "<group>"; };
CA5E5042209BDC5F001A8A81 /* MGLTestUtility.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MGLTestUtility.h; path = ../../darwin/test/MGLTestUtility.h; sourceTree = "<group>"; };
CA65C4F721E9BB080068B0D4 /* MGLCluster.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCluster.h; sourceTree = "<group>"; };
CA86FF0D22D8D5A0009EB14A /* MGLNetworkConfigurationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLNetworkConfigurationTests.m; sourceTree = "<group>"; };
CA6914B420E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLAnnotationViewIntegrationTests.mm; path = "Annotation Tests/MGLAnnotationViewIntegrationTests.mm"; sourceTree = "<group>"; };
CA86FF0D22D8D5A0009EB14A /* MGLNetworkConfigurationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLNetworkConfigurationTests.m; sourceTree = "<group>"; };
CA88DC2F21C85D900059ED5A /* MGLStyleURLIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLStyleURLIntegrationTest.m; sourceTree = "<group>"; };
CA8FBC0821A47BB100D1203C /* MGLRendererConfigurationTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLRendererConfigurationTests.mm; path = ../../darwin/test/MGLRendererConfigurationTests.mm; sourceTree = "<group>"; };
CAD9D0A922A86D6F001B25EE /* MGLResourceTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLResourceTests.mm; path = ../../darwin/test/MGLResourceTests.mm; sourceTree = "<group>"; };
@@ -1554,6 +1556,7 @@
CA88DC2F21C85D900059ED5A /* MGLStyleURLIntegrationTest.m */,
077061DB215DA11F000FEF62 /* MGLTestLocationManager.h */,
077061D9215DA00E000FEF62 /* MGLTestLocationManager.m */,
CA4F3BDD230F74C3008BAFEA /* MGLMapViewPendingBlockTests.m */,
);
path = "Integration Tests";
sourceTree = "<group>";
@@ -3196,6 +3199,7 @@
CA7766842229C11A0008DE9E /* SMCalloutView.m in Sources */,
CA34C9C3207FD272005C1A06 /* MGLCameraTransitionTests.mm in Sources */,
16376B0A1FFD9DAF0000563E /* MBGLIntegrationTests.mm in Sources */,
CA4F3BDE230F74C3008BAFEA /* MGLMapViewPendingBlockTests.m in Sources */,
CA88DC3021C85D900059ED5A /* MGLStyleURLIntegrationTest.m in Sources */,
CA0C27942076CA19001CE5B7 /* MGLMapViewIntegrationTest.m in Sources */,
CA7766832229C10E0008DE9E /* MGLCompactCalloutView.m in Sources */,

View File

@@ -257,6 +257,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;
@@ -625,6 +626,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
@@ -1046,6 +1052,34 @@ 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)scheduleTransitionCompletion:(dispatch_block_t)block
{
// Only add a block if the display link (that calls processPendingBlocks) is
// running, otherwise fall back to calling immediately.
if (_displayLink && !_displayLink.isPaused)
{
[self willChangeValueForKey:@"pendingCompletionBlocks"];
[self.pendingCompletionBlocks addObject:block];
[self didChangeValueForKey:@"pendingCompletionBlocks"];
return YES;
}
return NO;
}
#pragma mark - Life Cycle -
- (void)updateFromDisplayLink:(CADisplayLink *)displayLink
@@ -1070,7 +1104,7 @@ public:
return;
}
if (_needsDisplayRefresh)
if (_needsDisplayRefresh || (self.pendingCompletionBlocks.count > 0))
{
_needsDisplayRefresh = NO;
@@ -1079,6 +1113,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();
}
@@ -1145,6 +1186,7 @@ public:
{
[_displayLink invalidate];
_displayLink = nil;
[self processPendingBlocks];
}
}
@@ -1428,6 +1470,7 @@ public:
[MGLMapboxEvents flush];
_displayLink.paused = YES;
[self processPendingBlocks];
if ( ! self.glSnapshotView)
{
@@ -1480,6 +1523,11 @@ public:
{
super.hidden = hidden;
_displayLink.paused = hidden;
if (hidden)
{
[self processPendingBlocks];
}
}
- (void)tintColorDidChange
@@ -3340,26 +3388,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 scheduleTransitionCompletion: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;
}
@@ -3526,12 +3583,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 scheduleTransitionCompletion:completion])
{
completion();
});
}
};
animationOptions.transitionFinishFn = [pendingCompletion]() {
dispatch_async(dispatch_get_main_queue(), pendingCompletion);
};
}
@@ -3541,11 +3608,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;
}
@@ -3686,22 +3751,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 scheduleTransitionCompletion: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;
}
@@ -3757,22 +3830,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 scheduleTransitionCompletion: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;
}