Skip to content

Commit

Permalink
Merge pull request #29 from SDWebImage/bugfix_watchOS_animatedimage_s…
Browse files Browse the repository at this point in the history
…top_when_disappear

Fix that AnimatedImage on watchOS, does not stop animating when disappear and cause performance issue
  • Loading branch information
dreampiggy authored Oct 26, 2019
2 parents d9c1beb + 5582b33 commit 7d4030a
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 25 deletions.
13 changes: 7 additions & 6 deletions SDWebImageSwiftUI/Classes/AnimatedImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -231,21 +231,22 @@ public struct AnimatedImage : PlatformViewRepresentable {
if self.isAnimating != view.wrapped.animates {
view.wrapped.animates = self.isAnimating
}
#elseif os(iOS) || os(tvOS)
#else
if self.isAnimating != view.wrapped.isAnimating {
if self.isAnimating {
view.wrapped.startAnimating()
} else {
view.wrapped.stopAnimating()
}
}
#elseif os(watchOS)
if self.isAnimating {
view.wrapped.startAnimating()
} else {
view.wrapped.stopAnimating()
#if os(watchOS)
// when onAppear/onDisappear, SwiftUI will call this `updateView(_:context:)`
// we use this to start/stop animation, implements `SDAnimatedImageView` like behavior
DispatchQueue.main.async {
view.wrapped.updateAnimation()
}
#endif
#endif

configureView(view, context: context)
layoutView(view, context: context)
Expand Down
3 changes: 3 additions & 0 deletions SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ NS_ASSUME_NONNULL_BEGIN
/// Do not use this class directly in WatchKit or Storyboard. This class is implementation detail and will be removed in the future.
@interface SDAnimatedImageInterface : WKInterfaceImage

@property (nonatomic, assign, getter=isAnimating, readonly) BOOL animating;

- (instancetype)init WK_AVAILABLE_WATCHOS_ONLY(6.0);
- (void)setContentMode:(SDImageScaleMode)contentMode;
- (void)setAnimationRepeatCount:(nullable NSNumber *)repeatCount;
- (void)updateAnimation;

@end

Expand Down
77 changes: 58 additions & 19 deletions SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterface.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#pragma mark - SPI

#define kCGImageAnimationStatus_Uninitialized -1

@protocol CALayerProtocol <NSObject>
@property (nullable, strong) id contents;
@property CGFloat contentsScale;
Expand All @@ -24,6 +26,13 @@ @protocol CALayerProtocol <NSObject>
@protocol UIViewProtocol <NSObject>
@property (nonatomic, strong, readonly) id<CALayerProtocol> layer;
@property (nonatomic, assign) SDImageScaleMode contentMode;
@property (nonatomic, readonly) id<UIViewProtocol> superview;
@property (nonatomic, readonly, copy) NSArray<id<UIViewProtocol>> *subviews;
@property (nonatomic, readonly) id window;
@property (nonatomic) CGFloat alpha;
@property (nonatomic, getter=isHidden) BOOL hidden;
@property (nonatomic, getter=isOpaque) BOOL opaque;

@end

@interface WKInterfaceObject ()
Expand All @@ -44,6 +53,14 @@ @interface SDAnimatedImageStatus : NSObject

@implementation SDAnimatedImageStatus

- (instancetype)init {
self = [super init];
if (self) {
_animationStatus = kCGImageAnimationStatus_Uninitialized;
}
return self;
}

@end

@interface SDAnimatedImageInterface () {
Expand All @@ -59,6 +76,8 @@ @interface SDAnimatedImageInterface () {
@property (nonatomic, assign) CGFloat animatedImageScale;
@property (nonatomic, strong) SDAnimatedImageStatus *currentStatus;
@property (nonatomic, strong) NSNumber *animationRepeatCount;
@property (nonatomic, assign, getter=isAnimatedFormat) BOOL animatedFormat;
@property (nonatomic, assign, getter=isAnimating) BOOL animating;

@end

Expand Down Expand Up @@ -105,6 +124,8 @@ - (void)setImage:(UIImage *)image {
}
_image = image;

// Stop animating
[self stopBuiltInAnimation];
// Reset all value
[self resetAnimatedImage];

Expand All @@ -126,15 +147,29 @@ - (void)setImage:(UIImage *)image {
NSData *animatedImageData = animatedImage.animatedImageData;
SDImageFormat format = [NSData sd_imageFormatForImageData:animatedImageData];
if (format == SDImageFormatGIF || format == SDImageFormatPNG) {
[self startBuiltInAnimationWithImage:animatedImage];
self.animatedFormat = YES;
[self startBuiltInAnimation];
} else {
self.animatedFormat = NO;
[self stopBuiltInAnimation];
}

// Update should animate
[self updateShouldAnimate];
}
}

- (void)startBuiltInAnimationWithImage:(UIImage<SDAnimatedImage> *)animatedImage {
- (void)updateAnimation {
[self updateShouldAnimate];
if (self.currentStatus.shouldAnimate) {
[self startBuiltInAnimation];
} else {
[self stopBuiltInAnimation];
}
}

- (void)startBuiltInAnimation {
if (self.currentStatus && self.currentStatus.animationStatus == 0) {
return;
}
UIImage<SDAnimatedImage> *animatedImage = self.animatedImage;
NSData *animatedImageData = animatedImage.animatedImageData;
NSUInteger maxLoopCount;
if (self.animationRepeatCount != nil) {
Expand All @@ -148,7 +183,7 @@ - (void)startBuiltInAnimationWithImage:(UIImage<SDAnimatedImage> *)animatedImage
maxLoopCount = ((__bridge NSNumber *)kCFNumberPositiveInfinity).unsignedIntegerValue - 1;
}
NSDictionary *options = @{(__bridge NSString *)kCGImageAnimationLoopCount : @(maxLoopCount)};
SDAnimatedImageStatus *status = [SDAnimatedImageStatus new];
SDAnimatedImageStatus *status = [[SDAnimatedImageStatus alloc] init];
status.shouldAnimate = YES;
__weak typeof(self) wself = self;
status.animationStatus = CGAnimateImageDataWithBlock((__bridge CFDataRef)animatedImageData, (__bridge CFDictionaryRef)options, ^(size_t index, CGImageRef _Nonnull imageRef, bool * _Nonnull stop) {
Expand All @@ -171,6 +206,11 @@ - (void)startBuiltInAnimationWithImage:(UIImage<SDAnimatedImage> *)animatedImage
self.currentStatus = status;
}

- (void)stopBuiltInAnimation {
self.currentStatus.shouldAnimate = NO;
self.currentStatus.animationStatus = kCGImageAnimationStatus_Uninitialized;
}

- (void)displayLayer {
if (self.currentFrame) {
id<CALayerProtocol> layer = [self _interfaceView].layer;
Expand All @@ -184,44 +224,43 @@ - (void)resetAnimatedImage
self.animatedImage = nil;
self.totalFrameCount = 0;
self.totalLoopCount = 0;
// reset current state
self.currentStatus.shouldAnimate = NO;
self.currentStatus = nil;
[self resetCurrentFrameIndex];
self.animatedImageScale = 1;
}

- (void)resetCurrentFrameIndex
{
self.currentFrame = nil;
self.currentFrameIndex = 0;
self.currentLoopCount = 0;
self.animatedImageScale = 1;
self.animatedFormat = NO;
self.currentStatus = nil;
}

- (void)updateShouldAnimate
{
self.currentStatus.shouldAnimate = self.animatedImage && self.totalFrameCount > 1;
id<UIViewProtocol> view = [self _interfaceView];
BOOL isVisible = view.window && view.superview && ![view isHidden] && view.alpha > 0.0;
self.currentStatus.shouldAnimate = self.isAnimating && self.animatedImage && self.isAnimatedFormat && self.totalFrameCount > 1 && isVisible;
}

- (void)startAnimating {
self.animating = YES;
if (self.animatedImage) {
self.currentStatus.shouldAnimate = YES;
[self startBuiltInAnimation];
} else if (_image.images.count > 0) {
[super startAnimating];
}
}

- (void)startAnimatingWithImagesInRange:(NSRange)imageRange duration:(NSTimeInterval)duration repeatCount:(NSInteger)repeatCount {
self.animating = YES;
if (self.animatedImage) {
self.currentStatus.shouldAnimate = YES;
[self startBuiltInAnimation];
} else if (_image.images.count > 0) {
[super startAnimatingWithImagesInRange:imageRange duration:duration repeatCount:repeatCount];
}
}

- (void)stopAnimating {
self.animating = NO;
if (self.animatedImage) {
self.currentStatus.shouldAnimate = NO;
[self stopBuiltInAnimation];
} else if (_image.images.count > 0) {
[super stopAnimating];
}
Expand Down

0 comments on commit 7d4030a

Please sign in to comment.