[ios] Added pending completion blocks (#15122)
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
367
platform/ios/Integration Tests/MGLMapViewPendingBlockTests.m
Normal file
367
platform/ios/Integration Tests/MGLMapViewPendingBlockTests.m
Normal 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
|
||||
@@ -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 */,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user