Kaynağa Gözat

Merge pull request #2886 from dreampiggy/feature_runloop_mac

Support the runloop mode control for macOS.  Which can be useful when user want to pause animation when drag the mouse, or presenting modal window
DreamPiggy 6 yıl önce
ebeveyn
işleme
387944bbc1

+ 2 - 3
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

+ 14 - 1
SDWebImage/Core/SDAnimatedImageView.m

@@ -18,6 +18,7 @@
 
 @interface SDAnimatedImageView () <CALayerDelegate> {
     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

+ 12 - 3
SDWebImage/Private/SDDisplayLink.m

@@ -25,12 +25,13 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
 #if SD_MAC
 @property (nonatomic, assign) CVDisplayLinkRef displayLink;
 @property (nonatomic, assign) CVTimeStamp outputTime;
+@property (nonatomic, copy) NSRunLoopMode runloopMode;
 #elif SD_IOS || SD_TV
 @property (nonatomic, strong) CADisplayLink *displayLink;
 #else
 @property (nonatomic, strong) NSTimer *displayLink;
 @property (nonatomic, strong) NSRunLoop *runloop;
-@property (nonatomic, strong) NSRunLoopMode runloopMode;
+@property (nonatomic, copy) NSRunLoopMode runloopMode;
 @property (nonatomic, assign) NSTimeInterval currentFireDate;
 #endif
 
@@ -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,14 @@ 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)
+    NSString *runloopMode = self.runloopMode;
+    if (![runloopMode isEqualToString:NSRunLoopCommonModes] && ![runloopMode isEqualToString:NSRunLoop.mainRunLoop.currentMode]) {
+        return;
+    }
+#endif
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
     [_target performSelector:_selector withObject:self];