Quellcode durchsuchen

Merge pull request #2820 from dreampiggy/bugfix_sdanimatedimageview_mac_aligning

Fix the macOS that SDAnimatedImageView does not works for imageScaling and imageAlignment properties
DreamPiggy vor 6 Jahren
Ursprung
Commit
40b3ed4f4f
2 geänderte Dateien mit 29 neuen und 33 gelöschten Zeilen
  1. 2 0
      Examples/SDWebImage OSX Demo/ViewController.m
  2. 27 33
      SDWebImage/Core/SDAnimatedImageView.m

+ 2 - 0
Examples/SDWebImage OSX Demo/ViewController.m

@@ -41,6 +41,8 @@
     [self.imageView3 sd_setImageWithURL:[NSURL URLWithString:@"https://nr-platform.s3.amazonaws.com/uploads/platform/published_extension/branding_icon/275/AmazonS3.png"]];
     // SDAnimatedImageView + Animated Image
     self.imageView4.sd_imageTransition = SDWebImageTransition.fadeTransition;
+    self.imageView4.imageScaling = NSImageScaleProportionallyUpOrDown;
+    self.imageView4.imageAlignment = NSImageAlignLeft; // supports NSImageView's layout properties
     [self.imageView4 sd_setImageWithURL:[NSURL URLWithString:@"http://littlesvr.ca/apng/images/SteamEngine.webp"] placeholderImage:nil options:SDWebImageForceTransition];
     
     self.clearCacheButton.target = self;

+ 27 - 33
SDWebImage/Core/SDAnimatedImageView.m

@@ -65,6 +65,7 @@ static NSUInteger SDDeviceFreeMemory() {
 #else
 @property (nonatomic, strong) CADisplayLink *displayLink;
 #endif
+@property (nonatomic) CALayer *imageViewLayer; // The actual rendering layer.
 
 @end
 
@@ -132,10 +133,6 @@ static NSUInteger SDDeviceFreeMemory() {
     self.shouldIncrementalLoad = YES;
 #if SD_MAC
     self.wantsLayer = YES;
-    // Default value from `NSImageView`
-    self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
-    self.imageScaling = NSImageScaleProportionallyDown;
-    self.imageAlignment = NSImageAlignCenter;
 #endif
 #if SD_UIKIT
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
@@ -236,11 +233,8 @@ static NSUInteger SDDeviceFreeMemory() {
         if (self.shouldAnimate) {
             [self startAnimating];
         }
-        
-        [self.layer setNeedsDisplay];
-#if SD_MAC
-        [self.layer displayIfNeeded]; // macOS's imageViewLayer may not equal to self.layer. But `[super setImage:]` will impliedly mark it needsDisplay. We call `[self.layer displayIfNeeded]` to immediately refresh the imageViewLayer to avoid flashing
-#endif
+
+        [self.imageViewLayer setNeedsDisplay];
     }
 }
 
@@ -625,7 +619,7 @@ static NSUInteger SDDeviceFreeMemory() {
         self.currentFrame = currentFrame;
         self.currentFrameIndex = nextFrameIndex;
         self.bufferMiss = NO;
-        [self.layer setNeedsDisplay];
+        [self.imageViewLayer setNeedsDisplay];
     } else {
         self.bufferMiss = YES;
     }
@@ -705,42 +699,42 @@ static NSUInteger SDDeviceFreeMemory() {
 
 - (void)displayLayer:(CALayer *)layer
 {
-    if (_currentFrame) {
+    if (self.currentFrame) {
         layer.contentsScale = self.animatedImageScale;
-        layer.contents = (__bridge id)_currentFrame.CGImage;
+        layer.contents = (__bridge id)self.currentFrame.CGImage;
     }
 }
 
 #if SD_MAC
-// Layer-backed NSImageView optionally optimize to use a subview to do actual layer rendering.
-// When the optimization is turned on, it calls `updateLayer` instead of `displayLayer:` to update subview's layer.
-// When the optimization it turned off, this return nil and calls `displayLayer:` directly.
-- (CALayer *)imageViewLayer {
-    NSView *imageView = imageView = objc_getAssociatedObject(self, NSSelectorFromString(@"_imageView"));
+// NSImageView use a subview. We need this subview's layer for actual rendering.
+// Why using this design may because of properties like `imageAlignment` and `imageScaling`, which it's not available for UIImageView.contentMode (it's impossible to align left and keep aspect ratio at the same time)
+- (NSView *)imageView {
+    NSImageView *imageView = imageView = objc_getAssociatedObject(self, NSSelectorFromString(@"_imageView"));
     if (!imageView) {
         // macOS 10.14
         imageView = objc_getAssociatedObject(self, NSSelectorFromString(@"_imageSubview"));
     }
-    return imageView.layer;
+    return imageView;
 }
 
-- (void)updateLayer
-{
-    if (_currentFrame) {
-        [self displayLayer:self.imageViewLayer];
-    } else {
-        [super updateLayer];
+// on macOS, it's the imageView subview's layer (we use layer-hosting view to let CALayerDelegate works)
+- (CALayer *)imageViewLayer {
+    NSView *imageView = self.imageView;
+    if (!imageView) {
+        return nil;
     }
-}
-
-- (BOOL)wantsUpdateLayer {
-    // AppKit is different from UIKit, it need extra check before the layer is updated
-    // When we use the custom animation, the layer.setNeedsDisplay is directly called from display link (See `displayDidRefresh:`). However, for normal image rendering, we must implements and return YES to mark it need display
-    if (_currentFrame) {
-        return NO;
-    } else {
-        return YES;
+    if (!_imageViewLayer) {
+        _imageViewLayer = [CALayer new];
+        _imageViewLayer.delegate = self;
+        imageView.layer = _imageViewLayer;
+        imageView.wantsLayer = YES;
     }
+    return _imageViewLayer;
+}
+#else
+// on iOS, it's the imageView itself's layer
+- (CALayer *)imageViewLayer {
+    return self.layer;
 }
 
 #endif