From 323e038aafcc5302d6601dcad1ac92f769b550bb Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 6 Nov 2019 00:18:47 +0800 Subject: [PATCH] Support the runloop mode control for macOS. Which can be useful when user want to pause animation when drag the mouse, or open the Save/Open modal window --- SDWebImage/Core/SDAnimatedImageView.h | 5 ++--- SDWebImage/Core/SDAnimatedImageView.m | 15 ++++++++++++++- SDWebImage/Private/SDDisplayLink.m | 12 ++++++++++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/SDWebImage/Core/SDAnimatedImageView.h b/SDWebImage/Core/SDAnimatedImageView.h index 4f338826..2d9ac665 100644 --- a/SDWebImage/Core/SDAnimatedImageView.h +++ b/SDWebImage/Core/SDAnimatedImageView.h @@ -81,13 +81,12 @@ */ @property (nonatomic, assign) BOOL resetFrameIndexWhenStopped; -#if SD_UIKIT /** You can specify a runloop mode to let it rendering. - Default is NSRunLoopCommonModes on multi-core iOS device, NSDefaultRunLoopMode on single-core iOS device + Default is NSRunLoopCommonModes on multi-core device, NSDefaultRunLoopMode on single-core device + @note This is useful for some cases, for example, always specify NSDefaultRunLoopMode, if you want to pause the animation when user scroll (for Mac user, drag the mouse or touchpad) */ @property (nonatomic, copy, nonnull) NSRunLoopMode runLoopMode; -#endif @end #endif diff --git a/SDWebImage/Core/SDAnimatedImageView.m b/SDWebImage/Core/SDAnimatedImageView.m index f42a196d..c77fe2e8 100644 --- a/SDWebImage/Core/SDAnimatedImageView.m +++ b/SDWebImage/Core/SDAnimatedImageView.m @@ -18,6 +18,7 @@ @interface SDAnimatedImageView () { BOOL _initFinished; // Extra flag to mark the `commonInit` is called + NSRunLoopMode _runLoopMode; double _playbackRate; } @@ -149,6 +150,9 @@ self.player.totalLoopCount = self.animationRepeatCount; } + // RunLoop Mode + self.player.runLoopMode = self.runLoopMode; + // Play Rate self.player.playbackRate = self.playbackRate; @@ -188,12 +192,21 @@ - (void)setRunLoopMode:(NSRunLoopMode)runLoopMode { + _runLoopMode = [runLoopMode copy]; self.player.runLoopMode = runLoopMode; } - (NSRunLoopMode)runLoopMode { - return self.player.runLoopMode; + if (!_runLoopMode) { + _runLoopMode = [[self class] defaultRunLoopMode]; + } + return _runLoopMode; +} + ++ (NSString *)defaultRunLoopMode { + // Key off `activeProcessorCount` (as opposed to `processorCount`) since the system could shut down cores in certain situations. + return [NSProcessInfo processInfo].activeProcessorCount > 1 ? NSRunLoopCommonModes : NSDefaultRunLoopMode; } - (void)setPlaybackRate:(double)playbackRate diff --git a/SDWebImage/Private/SDDisplayLink.m b/SDWebImage/Private/SDDisplayLink.m index e248b803..c30e1238 100644 --- a/SDWebImage/Private/SDDisplayLink.m +++ b/SDWebImage/Private/SDDisplayLink.m @@ -25,6 +25,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt #if SD_MAC @property (nonatomic, assign) CVDisplayLinkRef displayLink; @property (nonatomic, assign) CVTimeStamp outputTime; +@property (nonatomic, strong) NSRunLoopMode runloopMode; #elif SD_IOS || SD_TV @property (nonatomic, strong) CADisplayLink *displayLink; #else @@ -118,7 +119,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt return; } #if SD_MAC - // CVDisplayLink does not use runloop + self.runloopMode = mode; #elif SD_IOS || SD_TV [self.displayLink addToRunLoop:runloop forMode:mode]; #else @@ -141,7 +142,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt return; } #if SD_MAC - // CVDisplayLink does not use runloop + self.runloopMode = nil; #elif SD_IOS || SD_TV [self.displayLink removeFromRunLoop:runloop forMode:mode]; #else @@ -186,6 +187,13 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt } - (void)displayLinkDidRefresh:(id)displayLink { +#if SD_MAC + // CVDisplayLink does not use runloop, but we can provide similar behavior for modes + // May use `default` runloop to avoid extra callback when in `eventTracking` (mouse drag, scroll) or `modalPanel` (modal panel) + if (self.runloopMode && self.runloopMode != NSRunLoopCommonModes && ![self.runloopMode isEqualToString:NSRunLoop.mainRunLoop.currentMode]) { + return; + } +#endif #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [_target performSelector:_selector withObject:self];