Use DisplayLink to prevent crashes in willResignActive (#68)
* Use DisplayLink to prevent crashes in willResignActive * Fixed screen freeze issue in the DisplayLink PR when bringing app back to foreground. Includes relevant changes from Mapbox PR (https://github.com/mapbox/mapbox-gl-native-ios/pull/432). Co-authored-by: Jukka Hietanen <jukka.hietanen@accelbit.com>
This commit is contained in:
@@ -248,7 +248,9 @@ public:
|
||||
@property (nonatomic) CGFloat scale;
|
||||
@property (nonatomic) CGFloat angle;
|
||||
@property (nonatomic) CGFloat quickZoomStart;
|
||||
/// Dormant means there is no underlying GL view (typically in the background)
|
||||
@property (nonatomic, getter=isDormant) BOOL dormant;
|
||||
@property (nonatomic, readonly, getter=isDisplayLinkActive) BOOL displayLinkActive;
|
||||
@property (nonatomic, readonly, getter=isRotationAllowed) BOOL rotationAllowed;
|
||||
@property (nonatomic) CGFloat rotationThresholdWhileZooming;
|
||||
@property (nonatomic) CGFloat rotationBeforeThresholdMet;
|
||||
@@ -283,6 +285,11 @@ public:
|
||||
@property (nonatomic, assign) UIEdgeInsets safeMapViewContentInsets;
|
||||
@property (nonatomic, strong) NSNumber *automaticallyAdjustContentInsetHolder;
|
||||
|
||||
// Display Link
|
||||
@property (nonatomic, weak) UIScreen *displayLinkScreen;
|
||||
@property (nonatomic) CADisplayLink *displayLink;
|
||||
@property (nonatomic, assign) BOOL needsDisplayRefresh;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MGLMapView
|
||||
@@ -313,9 +320,6 @@ public:
|
||||
CLLocationDegrees _pendingLatitude;
|
||||
CLLocationDegrees _pendingLongitude;
|
||||
|
||||
CADisplayLink *_displayLink;
|
||||
BOOL _needsDisplayRefresh;
|
||||
|
||||
NSInteger _changeDelimiterSuppressionDepth;
|
||||
|
||||
/// Center of the pinch gesture on the previous iteration of the gesture.
|
||||
@@ -495,7 +499,7 @@ public:
|
||||
NSAssert(!_mbglMap, @"_mbglMap should be NULL");
|
||||
_mbglMap = std::make_unique<mbgl::Map>(*_rendererFrontend, *_mbglView, mapOptions, resourceOptions);
|
||||
|
||||
// start paused if in IB
|
||||
// start paused if launch into the background
|
||||
if (background) {
|
||||
self.dormant = YES;
|
||||
}
|
||||
@@ -730,7 +734,7 @@ public:
|
||||
[self removeAnnotations:annotations];
|
||||
}
|
||||
|
||||
[self validateDisplayLink];
|
||||
[self destroyDisplayLink];
|
||||
|
||||
[self destroyCoreObjects];
|
||||
|
||||
@@ -1179,7 +1183,7 @@ public:
|
||||
{
|
||||
// Only add a block if the display link (that calls processPendingBlocks) is
|
||||
// running, otherwise fall back to calling immediately.
|
||||
if (_displayLink && !_displayLink.isPaused)
|
||||
if (self.isDisplayLinkActive)
|
||||
{
|
||||
[self willChangeValueForKey:@"pendingCompletionBlocks"];
|
||||
[self.pendingCompletionBlocks addObject:block];
|
||||
@@ -1245,9 +1249,9 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
if (_needsDisplayRefresh || (self.pendingCompletionBlocks.count > 0))
|
||||
if (self.needsDisplayRefresh || (self.pendingCompletionBlocks.count > 0))
|
||||
{
|
||||
_needsDisplayRefresh = NO;
|
||||
self.needsDisplayRefresh = NO;
|
||||
|
||||
// UIView update logic has moved into `renderSync` above, which now gets
|
||||
// triggered by a call to setNeedsDisplay.
|
||||
@@ -1282,7 +1286,7 @@ public:
|
||||
{
|
||||
MGLAssertIsMainThread();
|
||||
|
||||
_needsDisplayRefresh = YES;
|
||||
self.needsDisplayRefresh = YES;
|
||||
}
|
||||
|
||||
- (void)willTerminate
|
||||
@@ -1291,14 +1295,50 @@ public:
|
||||
|
||||
if ( ! self.dormant)
|
||||
{
|
||||
[self validateDisplayLink];
|
||||
[self.displayLink invalidate];
|
||||
self.displayLink = nil;
|
||||
|
||||
self.dormant = YES;
|
||||
|
||||
if (_rendererFrontend) {
|
||||
_rendererFrontend->reduceMemoryUse();
|
||||
}
|
||||
|
||||
_mbglView->deleteView();
|
||||
}
|
||||
|
||||
[self destroyCoreObjects];
|
||||
}
|
||||
|
||||
- (UIScreen *)windowScreen {
|
||||
UIScreen *screen;
|
||||
|
||||
#ifdef SUPPORT_UIWINDOWSCENE
|
||||
if (@available(iOS 13.0, *)) {
|
||||
if (self.window.windowScene) {
|
||||
screen = self.window.windowScene.screen;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Fallback if there's no windowScene
|
||||
if (!screen) {
|
||||
screen = self.window.screen;
|
||||
}
|
||||
|
||||
return screen;
|
||||
}
|
||||
|
||||
- (BOOL)isVisible
|
||||
{
|
||||
// "Visible" is not strictly true here - for example, the view hierarchy is not
|
||||
// currently observed (e.g. looking at a parent's or the window's hidden
|
||||
// status.
|
||||
// This does NOT take application state into account
|
||||
UIScreen *screen = [self windowScreen];
|
||||
return (!self.isHidden && screen);
|
||||
}
|
||||
|
||||
- (void)validateDisplayLink
|
||||
{
|
||||
BOOL isVisible = self.superview && self.window;
|
||||
@@ -1312,7 +1352,7 @@ public:
|
||||
_displayLink = [self.window.screen displayLinkWithTarget:self selector:@selector(updateFromDisplayLink:)];
|
||||
[self updateDisplayLinkPreferredFramesPerSecond];
|
||||
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
|
||||
_needsDisplayRefresh = YES;
|
||||
self.needsDisplayRefresh = YES;
|
||||
[self updateFromDisplayLink:_displayLink];
|
||||
}
|
||||
else if ( ! isVisible && _displayLink)
|
||||
@@ -1395,22 +1435,47 @@ public:
|
||||
// slow down. The exact cause of this is unknown, but this work around
|
||||
// appears to lessen the effects.
|
||||
_mbglView->setPresentsWithTransaction(NO);
|
||||
}
|
||||
|
||||
// Changing windows regardless of whether it's a new one, or the map is being
|
||||
// removed from the hierarchy
|
||||
[self destroyDisplayLink];
|
||||
|
||||
// Moved from didMoveToWindow
|
||||
[self validateDisplayLink];
|
||||
if (self.window) {
|
||||
#ifdef SUPPORT_UIWINDOWSCENE
|
||||
if (@available(iOS 13.0, *))
|
||||
{
|
||||
[self.window removeObserver:self forKeyPath:@"windowScene" context:windowScreenContext];
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
[self.window removeObserver:self forKeyPath:@"screen" context:windowScreenContext];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didMoveToWindow
|
||||
{
|
||||
[super didMoveToWindow];
|
||||
|
||||
MGLLogDebug(@"[%p] window=%p", self, self.window);
|
||||
|
||||
if (self.window)
|
||||
{
|
||||
// See above comment
|
||||
_mbglView->setPresentsWithTransaction(self.enablePresentsWithTransaction);
|
||||
[self resumeRenderingIfNecessary];
|
||||
[self updatePresentsWithTransaction];
|
||||
|
||||
[self validateDisplayLink];
|
||||
#ifdef SUPPORT_UIWINDOWSCENE
|
||||
if (@available(iOS 13.0, *))
|
||||
{
|
||||
[self.window addObserver:self forKeyPath:@"windowScene" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:windowScreenContext];
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
[self.window addObserver:self forKeyPath:@"screen" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:windowScreenContext];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1518,16 +1583,75 @@ public:
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)stopDisplayLink
|
||||
{
|
||||
MGLLogDebug(@"[%p]", self);
|
||||
self.displayLink.paused = YES;
|
||||
self.needsDisplayRefresh = NO;
|
||||
[self processPendingBlocks];
|
||||
}
|
||||
|
||||
- (void)createDisplayLink
|
||||
{
|
||||
MGLLogDebug(@"[%p]", self);
|
||||
|
||||
// Create and start the display link in a *paused* state
|
||||
MGLAssert(!self.displayLinkScreen, @"");
|
||||
MGLAssert(!self.displayLink, @"");
|
||||
MGLAssert(self.window, @"");
|
||||
MGLAssert(self.window.screen, @"");
|
||||
|
||||
self.displayLinkScreen = self.window.screen;
|
||||
self.displayLink = [self.window.screen displayLinkWithTarget:self selector:@selector(updateFromDisplayLink:)];
|
||||
self.displayLink.paused = YES;
|
||||
|
||||
[self updateDisplayLinkPreferredFramesPerSecond];
|
||||
|
||||
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
|
||||
|
||||
if (_mbglMap && self.mbglMap.getMapOptions().constrainMode() == mbgl::ConstrainMode::None)
|
||||
{
|
||||
self.mbglMap.setConstrainMode(mbgl::ConstrainMode::HeightOnly);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)destroyDisplayLink
|
||||
{
|
||||
MGLLogDebug(@"[%p]", self);
|
||||
[self.displayLink invalidate];
|
||||
self.displayLink = nil;
|
||||
self.displayLinkScreen = nil;
|
||||
self.needsDisplayRefresh = NO;
|
||||
[self processPendingBlocks];
|
||||
}
|
||||
|
||||
- (void)startDisplayLink
|
||||
{
|
||||
MGLLogDebug(@"[%p]", self);
|
||||
MGLAssert(self.displayLink, @"");
|
||||
MGLAssert([self isVisible], @"Display link should only be started when allowed");
|
||||
|
||||
self.displayLink.paused = NO;
|
||||
[self setNeedsRerender];
|
||||
[self updateFromDisplayLink:self.displayLink];
|
||||
}
|
||||
|
||||
#pragma mark - Application lifecycle
|
||||
- (void)willResignActive:(NSNotification *)notification
|
||||
{
|
||||
if ([self supportsBackgroundRendering])
|
||||
{
|
||||
MGLAssertIsMainThread();
|
||||
MGLLogDebug(@"[%p]", self);
|
||||
|
||||
// Going from active to inactive states. This could be because a system dialog
|
||||
// has been displayed, control center, or the app is headed into the background
|
||||
|
||||
if (self.supportsBackgroundRendering) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.lastSnapshotImage = _mbglView->snapshot();
|
||||
|
||||
|
||||
// We want to pause the rendering
|
||||
[self stopDisplayLink];
|
||||
|
||||
// For OpenGL this calls glFinish as recommended in
|
||||
// https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/ImplementingaMultitasking-awareOpenGLESApplication/ImplementingaMultitasking-awareOpenGLESApplication.html#//apple_ref/doc/uid/TP40008793-CH5-SW1
|
||||
// reduceMemoryUse(), calls performCleanup(), which calls glFinish
|
||||
@@ -1539,18 +1663,83 @@ public:
|
||||
|
||||
- (void)didEnterBackground:(NSNotification *)notification
|
||||
{
|
||||
[self pauseRendering:notification];
|
||||
MGLAssertIsMainThread();
|
||||
MGLAssert(!self.dormant, @"Should not be dormant heading into background");
|
||||
MGLLogDebug(@"[%p] dormant=%d", self, self.dormant);
|
||||
|
||||
// See comment in `supportsBackgroundRendering` above.
|
||||
if (self.supportsBackgroundRendering) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We now want to stop rendering.
|
||||
// We want to pause the rendering
|
||||
[self stopDisplayLink];
|
||||
|
||||
// For OpenGL this calls glFinish as recommended in
|
||||
// https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/ImplementingaMultitasking-awareOpenGLESApplication/ImplementingaMultitasking-awareOpenGLESApplication.html#//apple_ref/doc/uid/TP40008793-CH5-SW1
|
||||
// reduceMemoryUse(), calls performCleanup(), which calls glFinish
|
||||
if (_rendererFrontend)
|
||||
{
|
||||
_rendererFrontend->reduceMemoryUse();
|
||||
}
|
||||
|
||||
// We now completely remove the display link, and the renderable resource.
|
||||
// Although the method below is called `deleteView` this does NOT delete the
|
||||
// GLKView, instead releasing the memory hungry resources.
|
||||
[self destroyDisplayLink];
|
||||
[self processPendingBlocks];
|
||||
_mbglView->deleteView();
|
||||
|
||||
self.dormant = YES;
|
||||
|
||||
// We want to add a snapshot image over the top of the map view, so that
|
||||
// there are no glitches when the application comes back into the foreground
|
||||
|
||||
[self enableSnapshotView];
|
||||
|
||||
// Handle non-rendering issues.
|
||||
[self validateLocationServices];
|
||||
}
|
||||
|
||||
- (void)willEnterForeground:(NSNotification *)notification
|
||||
{
|
||||
// Do nothing, currently if resumeRendering is called here it's a no-op.
|
||||
MGLLogDebug(@"[%p] dormant=%d", self, self.dormant);
|
||||
|
||||
// We're transitioning from Background to Inactive states
|
||||
|
||||
if (self.supportsBackgroundRendering) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reverse the process of going into the background
|
||||
_mbglView->createView();
|
||||
|
||||
// A display link needs the window's screen, so create it if we can
|
||||
UIScreen *screen = [self windowScreen];
|
||||
|
||||
if (screen) {
|
||||
[self createDisplayLink];
|
||||
|
||||
// If we can render during the inactive state, start the display link now
|
||||
if (self.isVisible) {
|
||||
[self startDisplayLink];
|
||||
}
|
||||
}
|
||||
self.dormant = NO;
|
||||
// Note: We do not remove the snapshot view (if there is one) until we have become
|
||||
// active.
|
||||
[self validateLocationServices];
|
||||
}
|
||||
|
||||
- (void)didBecomeActive:(NSNotification *)notification
|
||||
{
|
||||
[self resumeRendering:notification];
|
||||
self.lastSnapshotImage = nil;
|
||||
MGLLogDebug(@"[%p] DL.paused=<%p>.paused=%d", self, self.displayLink, self.displayLink.paused);
|
||||
|
||||
// Most times, we should already have a display link created at this point,
|
||||
// which may or may not be running. However, at the start of the application,
|
||||
// it's possible to have a situation where the display link hasn't been created.
|
||||
[self resumeRenderingIfNecessary];
|
||||
}
|
||||
|
||||
#pragma mark - GL / display link wake/sleep
|
||||
@@ -1561,106 +1750,108 @@ public:
|
||||
|
||||
- (BOOL)supportsBackgroundRendering
|
||||
{
|
||||
// Note: The following comment may be out of date with Metal enabled builds where
|
||||
// OpenGL is being emulated with Metal.
|
||||
//
|
||||
// If this view targets an external display, such as AirPlay or CarPlay, we
|
||||
// can safely continue to render OpenGL content without tripping
|
||||
// gpus_ReturnNotPermittedKillClient in libGPUSupportMercury, because the
|
||||
// external connection keeps the application from truly receding to the
|
||||
// background.
|
||||
return (self.window.screen != [UIScreen mainScreen]);
|
||||
UIScreen *screen = [self windowScreen];
|
||||
|
||||
BOOL supportsBackgroundRendering = (screen && (screen != [UIScreen mainScreen]));
|
||||
MGLLogDebug(@"supportsBackgroundRendering=%d",supportsBackgroundRendering);
|
||||
return supportsBackgroundRendering;
|
||||
}
|
||||
|
||||
- (void)pauseRendering:(__unused NSNotification *)notification
|
||||
{
|
||||
// If this view targets an external display, such as AirPlay or CarPlay, we
|
||||
// can safely continue to render OpenGL content without tripping
|
||||
// gpus_ReturnNotPermittedKillClient in libGPUSupportMercury, because the
|
||||
// external connection keeps the application from truly receding to the
|
||||
// background.
|
||||
if ([self supportsBackgroundRendering])
|
||||
- (void)enableSnapshotView {
|
||||
if (self.lastSnapshotImage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MGLLogInfo(@"Entering background.");
|
||||
MGLAssertIsMainThread();
|
||||
|
||||
// Ideally we would wait until we actually received a memory warning but the bulk of the memory
|
||||
// we have to release is tied up in GL buffers that we can't touch once we're in the background.
|
||||
// Compromise position: release everything but currently rendering tiles
|
||||
// A possible improvement would be to store a copy of the GL buffers that we could use to rapidly
|
||||
// restart, but that we could also discard in response to a memory warning.
|
||||
if (_rendererFrontend)
|
||||
{
|
||||
_rendererFrontend->reduceMemoryUse();
|
||||
}
|
||||
|
||||
if ( ! self.dormant)
|
||||
{
|
||||
self.dormant = YES;
|
||||
|
||||
[self validateLocationServices];
|
||||
|
||||
_displayLink.paused = YES;
|
||||
[self processPendingBlocks];
|
||||
|
||||
if (self.lastSnapshotImage)
|
||||
if ( ! self.glSnapshotView)
|
||||
{
|
||||
if ( ! self.glSnapshotView)
|
||||
{
|
||||
self.glSnapshotView = [[UIImageView alloc] initWithFrame: _mbglView->getView().frame];
|
||||
self.glSnapshotView.autoresizingMask = _mbglView->getView().autoresizingMask;
|
||||
self.glSnapshotView.contentMode = UIViewContentModeCenter;
|
||||
[self insertSubview:self.glSnapshotView aboveSubview:_mbglView->getView()];
|
||||
}
|
||||
|
||||
self.glSnapshotView.image = self.lastSnapshotImage;
|
||||
self.glSnapshotView.hidden = NO;
|
||||
self.glSnapshotView.alpha = 1;
|
||||
|
||||
if (self.debugMask && [self.glSnapshotView.subviews count] == 0)
|
||||
{
|
||||
UIView *snapshotTint = [[UIView alloc] initWithFrame:self.glSnapshotView.bounds];
|
||||
snapshotTint.autoresizingMask = self.glSnapshotView.autoresizingMask;
|
||||
snapshotTint.backgroundColor = [[UIColor redColor] colorWithAlphaComponent:0.25];
|
||||
[self.glSnapshotView addSubview:snapshotTint];
|
||||
}
|
||||
self.glSnapshotView = [[UIImageView alloc] initWithFrame: _mbglView->getView().frame];
|
||||
self.glSnapshotView.autoresizingMask = _mbglView->getView().autoresizingMask;
|
||||
self.glSnapshotView.contentMode = UIViewContentModeCenter;
|
||||
[self insertSubview:self.glSnapshotView aboveSubview:_mbglView->getView()];
|
||||
}
|
||||
|
||||
_mbglView->deleteView();
|
||||
self.glSnapshotView.image = self.lastSnapshotImage;
|
||||
self.glSnapshotView.hidden = NO;
|
||||
self.glSnapshotView.opaque = NO;
|
||||
self.glSnapshotView.alpha = 1;
|
||||
|
||||
if (self.debugMask && [self.glSnapshotView.subviews count] == 0)
|
||||
{
|
||||
UIView *snapshotTint = [[UIView alloc] initWithFrame:self.glSnapshotView.bounds];
|
||||
snapshotTint.autoresizingMask = self.glSnapshotView.autoresizingMask;
|
||||
snapshotTint.backgroundColor = [[UIColor redColor] colorWithAlphaComponent:0.25];
|
||||
[self.glSnapshotView addSubview:snapshotTint];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resumeRendering:(__unused NSNotification *)notification
|
||||
{
|
||||
MGLLogInfo(@"Entering foreground.");
|
||||
MGLAssertIsMainThread();
|
||||
- (void)resumeRenderingIfNecessary {
|
||||
MGLLogDebug(@"[%p] DL.paused=<%p>.paused=%d", self, self.displayLink, self.displayLink.paused);
|
||||
|
||||
if (self.dormant && [UIApplication sharedApplication].applicationState != UIApplicationStateBackground)
|
||||
{
|
||||
self.dormant = NO;
|
||||
// Most times, we should already have a display link created at this point,
|
||||
// which may or may not be running. However, at the start of the application,
|
||||
// it's possible to have a situation where the display link hasn't been created.
|
||||
|
||||
_mbglView->createView();
|
||||
// Reverse the process of going into the background
|
||||
if ([UIApplication sharedApplication].applicationState != UIApplicationStateBackground) {
|
||||
if (self.dormant) {
|
||||
_mbglView->createView();
|
||||
self.dormant = NO;
|
||||
}
|
||||
|
||||
[UIView transitionWithView:self
|
||||
duration:0.25
|
||||
options:UIViewAnimationOptionTransitionCrossDissolve
|
||||
animations:^{
|
||||
self.glSnapshotView.hidden = YES;
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
[self.glSnapshotView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
|
||||
}];
|
||||
|
||||
_displayLink.paused = NO;
|
||||
|
||||
[self validateLocationServices];
|
||||
// Check display link, if necessary
|
||||
if (!self.displayLink) {
|
||||
if ([self windowScreen]) {
|
||||
[self createDisplayLink];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start the display link if we need to
|
||||
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive || [UIApplication sharedApplication].applicationState == UIApplicationStateInactive) {
|
||||
|
||||
BOOL mapViewVisible = self.isVisible;
|
||||
if (self.displayLink) {
|
||||
if (mapViewVisible && self.displayLink.isPaused) {
|
||||
[self startDisplayLink];
|
||||
}
|
||||
else if (!mapViewVisible && !self.displayLink.isPaused) {
|
||||
// Unlikely scenario
|
||||
[self stopDisplayLink];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reveal the snapshot view
|
||||
|
||||
if (self.glSnapshotView && !self.glSnapshotView.isHidden) {
|
||||
[UIView transitionWithView:self
|
||||
duration:0.25
|
||||
options:UIViewAnimationOptionTransitionCrossDissolve
|
||||
animations:^{
|
||||
self.glSnapshotView.hidden = YES;
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
[self.glSnapshotView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isDisplayLinkActive {
|
||||
MGLLogDebug(@"[%p]", self);
|
||||
return (self.displayLink && !self.displayLink.isPaused);
|
||||
}
|
||||
|
||||
- (void)setHidden:(BOOL)hidden
|
||||
{
|
||||
super.hidden = hidden;
|
||||
_displayLink.paused = hidden;
|
||||
_displayLink.paused = ![self isVisible];
|
||||
|
||||
if (hidden)
|
||||
{
|
||||
@@ -2585,6 +2776,8 @@ public:
|
||||
|
||||
#pragma mark - Properties -
|
||||
|
||||
static void *windowScreenContext = &windowScreenContext;
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||||
{
|
||||
if ([keyPath isEqualToString:@"coordinate"] && [object conformsToProtocol:@protocol(MGLAnnotation)] && ![object isKindOfClass:[MGLMultiPoint class]])
|
||||
@@ -2635,6 +2828,14 @@ public:
|
||||
[self updateCalloutView];
|
||||
}
|
||||
}
|
||||
else if (context == windowScreenContext)
|
||||
{
|
||||
if ([keyPath isEqualToString:@"screen"] ||
|
||||
[keyPath isEqualToString:@"windowScene"]) {
|
||||
[self destroyDisplayLink];
|
||||
[self didBecomeActive:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSSet<NSString *> *)keyPathsForValuesAffectingZoomEnabled
|
||||
@@ -5089,7 +5290,7 @@ public:
|
||||
// of the `-[MGLCalloutView marginInsetsHintForPresentationFromRect:]
|
||||
// protocol method.
|
||||
bounds = CGRectInset(bounds, -calloutPositioningRect.size.width, -calloutPositioningRect.size.height);
|
||||
}
|
||||
}
|
||||
|
||||
if (moveIntoView)
|
||||
{
|
||||
@@ -6990,6 +7191,7 @@ public:
|
||||
NSTimeInterval now = CACurrentMediaTime();
|
||||
|
||||
if (lastSnapshotTime == 0.0 || (now - lastSnapshotTime > MGLBackgroundSnapshotImageInterval)) {
|
||||
MGLLogDebug(@"Taking snapshot");
|
||||
self.lastSnapshotImage = _mbglView->snapshot();
|
||||
lastSnapshotTime = now;
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const _Nonnull MGLUnderlyingMapUna
|
||||
@property (nonatomic, nonnull) MGLUserLocationAnnotationView *userLocationAnnotationView;
|
||||
@property (nonatomic, nonnull) MGLAnnotationContainerView *annotationContainerView;
|
||||
@property (nonatomic, readonly) BOOL enablePresentsWithTransaction;
|
||||
@property (nonatomic, assign) BOOL needsDisplayRefresh;
|
||||
|
||||
- (BOOL) _opaque;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user