Skip to content

Commit c63ea61

Browse files
committed
Fix the issue that on watchOS, AnimatedImage when disappear, the CGImage decoding and animation does not stop issue. Increase performance
1 parent d9c1beb commit c63ea61

File tree

3 files changed

+68
-26
lines changed

3 files changed

+68
-26
lines changed

SDWebImageSwiftUI/Classes/AnimatedImage.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,6 @@ public struct AnimatedImage : PlatformViewRepresentable {
194194
self.imageModel.progressBlock?(receivedSize, expectedSize)
195195
}) { (image, error, cacheType, _) in
196196
if let image = image {
197-
self.imageModel.image = image
198197
self.imageModel.successBlock?(image, cacheType)
199198
} else {
200199
self.imageModel.failureBlock?(error ?? NSError())
@@ -231,21 +230,22 @@ public struct AnimatedImage : PlatformViewRepresentable {
231230
if self.isAnimating != view.wrapped.animates {
232231
view.wrapped.animates = self.isAnimating
233232
}
234-
#elseif os(iOS) || os(tvOS)
233+
#else
235234
if self.isAnimating != view.wrapped.isAnimating {
236235
if self.isAnimating {
237236
view.wrapped.startAnimating()
238237
} else {
239238
view.wrapped.stopAnimating()
240239
}
241240
}
242-
#elseif os(watchOS)
243-
if self.isAnimating {
244-
view.wrapped.startAnimating()
245-
} else {
246-
view.wrapped.stopAnimating()
241+
#if os(watchOS)
242+
// when onAppear/onDisappear, SwiftUI will call this `updateView(_:context:)`
243+
// we use this to start/stop animation, implements `SDAnimatedImageView` like behavior
244+
DispatchQueue.main.async {
245+
view.wrapped.updateAnimation()
247246
}
248247
#endif
248+
#endif
249249

250250
configureView(view, context: context)
251251
layoutView(view, context: context)

SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterface.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@ NS_ASSUME_NONNULL_BEGIN
1414
/// Do not use this class directly in WatchKit or Storyboard. This class is implementation detail and will be removed in the future.
1515
@interface SDAnimatedImageInterface : WKInterfaceImage
1616

17+
@property (nonatomic, assign, getter=isAnimating, readonly) BOOL animating;
18+
1719
- (instancetype)init WK_AVAILABLE_WATCHOS_ONLY(6.0);
1820
- (void)setContentMode:(SDImageScaleMode)contentMode;
1921
- (void)setAnimationRepeatCount:(nullable NSNumber *)repeatCount;
22+
- (void)updateAnimation;
2023

2124
@end
2225

SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterface.m

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
#pragma mark - SPI
1818

19+
#define kCGImageAnimationStatus_Uninitialized -1
20+
1921
@protocol CALayerProtocol <NSObject>
2022
@property (nullable, strong) id contents;
2123
@property CGFloat contentsScale;
@@ -24,6 +26,13 @@ @protocol CALayerProtocol <NSObject>
2426
@protocol UIViewProtocol <NSObject>
2527
@property (nonatomic, strong, readonly) id<CALayerProtocol> layer;
2628
@property (nonatomic, assign) SDImageScaleMode contentMode;
29+
@property (nonatomic, readonly) id<UIViewProtocol> superview;
30+
@property (nonatomic, readonly, copy) NSArray<id<UIViewProtocol>> *subviews;
31+
@property (nonatomic, readonly) id window;
32+
@property (nonatomic) CGFloat alpha;
33+
@property (nonatomic, getter=isHidden) BOOL hidden;
34+
@property (nonatomic, getter=isOpaque) BOOL opaque;
35+
2736
@end
2837

2938
@interface WKInterfaceObject ()
@@ -44,6 +53,14 @@ @interface SDAnimatedImageStatus : NSObject
4453

4554
@implementation SDAnimatedImageStatus
4655

56+
- (instancetype)init {
57+
self = [super init];
58+
if (self) {
59+
_animationStatus = kCGImageAnimationStatus_Uninitialized;
60+
}
61+
return self;
62+
}
63+
4764
@end
4865

4966
@interface SDAnimatedImageInterface () {
@@ -59,6 +76,8 @@ @interface SDAnimatedImageInterface () {
5976
@property (nonatomic, assign) CGFloat animatedImageScale;
6077
@property (nonatomic, strong) SDAnimatedImageStatus *currentStatus;
6178
@property (nonatomic, strong) NSNumber *animationRepeatCount;
79+
@property (nonatomic, assign, getter=isAnimatedFormat) BOOL animatedFormat;
80+
@property (nonatomic, assign, getter=isAnimating) BOOL animating;
6281

6382
@end
6483

@@ -105,6 +124,8 @@ - (void)setImage:(UIImage *)image {
105124
}
106125
_image = image;
107126

127+
// Stop animating
128+
[self stopBuiltInAnimation];
108129
// Reset all value
109130
[self resetAnimatedImage];
110131

@@ -126,15 +147,29 @@ - (void)setImage:(UIImage *)image {
126147
NSData *animatedImageData = animatedImage.animatedImageData;
127148
SDImageFormat format = [NSData sd_imageFormatForImageData:animatedImageData];
128149
if (format == SDImageFormatGIF || format == SDImageFormatPNG) {
129-
[self startBuiltInAnimationWithImage:animatedImage];
150+
self.animatedFormat = YES;
151+
[self startBuiltInAnimation];
152+
} else {
153+
self.animatedFormat = NO;
154+
[self stopBuiltInAnimation];
130155
}
131-
132-
// Update should animate
133-
[self updateShouldAnimate];
134156
}
135157
}
136158

137-
- (void)startBuiltInAnimationWithImage:(UIImage<SDAnimatedImage> *)animatedImage {
159+
- (void)updateAnimation {
160+
[self updateShouldAnimate];
161+
if (self.currentStatus.shouldAnimate) {
162+
[self startBuiltInAnimation];
163+
} else {
164+
[self stopBuiltInAnimation];
165+
}
166+
}
167+
168+
- (void)startBuiltInAnimation {
169+
if (self.currentStatus && self.currentStatus.animationStatus == 0) {
170+
return;
171+
}
172+
UIImage<SDAnimatedImage> *animatedImage = self.animatedImage;
138173
NSData *animatedImageData = animatedImage.animatedImageData;
139174
NSUInteger maxLoopCount;
140175
if (self.animationRepeatCount != nil) {
@@ -148,7 +183,7 @@ - (void)startBuiltInAnimationWithImage:(UIImage<SDAnimatedImage> *)animatedImage
148183
maxLoopCount = ((__bridge NSNumber *)kCFNumberPositiveInfinity).unsignedIntegerValue - 1;
149184
}
150185
NSDictionary *options = @{(__bridge NSString *)kCGImageAnimationLoopCount : @(maxLoopCount)};
151-
SDAnimatedImageStatus *status = [SDAnimatedImageStatus new];
186+
SDAnimatedImageStatus *status = [[SDAnimatedImageStatus alloc] init];
152187
status.shouldAnimate = YES;
153188
__weak typeof(self) wself = self;
154189
status.animationStatus = CGAnimateImageDataWithBlock((__bridge CFDataRef)animatedImageData, (__bridge CFDictionaryRef)options, ^(size_t index, CGImageRef _Nonnull imageRef, bool * _Nonnull stop) {
@@ -171,6 +206,11 @@ - (void)startBuiltInAnimationWithImage:(UIImage<SDAnimatedImage> *)animatedImage
171206
self.currentStatus = status;
172207
}
173208

209+
- (void)stopBuiltInAnimation {
210+
self.currentStatus.shouldAnimate = NO;
211+
self.currentStatus.animationStatus = kCGImageAnimationStatus_Uninitialized;
212+
}
213+
174214
- (void)displayLayer {
175215
if (self.currentFrame) {
176216
id<CALayerProtocol> layer = [self _interfaceView].layer;
@@ -184,44 +224,43 @@ - (void)resetAnimatedImage
184224
self.animatedImage = nil;
185225
self.totalFrameCount = 0;
186226
self.totalLoopCount = 0;
187-
// reset current state
188-
self.currentStatus.shouldAnimate = NO;
189-
self.currentStatus = nil;
190-
[self resetCurrentFrameIndex];
191-
self.animatedImageScale = 1;
192-
}
193-
194-
- (void)resetCurrentFrameIndex
195-
{
196227
self.currentFrame = nil;
197228
self.currentFrameIndex = 0;
198229
self.currentLoopCount = 0;
230+
self.animatedImageScale = 1;
231+
self.animatedFormat = NO;
232+
self.currentStatus = nil;
199233
}
200234

201235
- (void)updateShouldAnimate
202236
{
203-
self.currentStatus.shouldAnimate = self.animatedImage && self.totalFrameCount > 1;
237+
id<UIViewProtocol> view = [self _interfaceView];
238+
BOOL isVisible = view.window && view.superview && ![view isHidden] && view.alpha > 0.0;
239+
self.currentStatus.shouldAnimate = self.animatedImage && self.totalFrameCount > 1 && self.isAnimatedFormat && isVisible;
204240
}
205241

206242
- (void)startAnimating {
243+
self.animating = YES;
207244
if (self.animatedImage) {
208-
self.currentStatus.shouldAnimate = YES;
245+
[self startBuiltInAnimation];
209246
} else if (_image.images.count > 0) {
210247
[super startAnimating];
211248
}
212249
}
213250

214251
- (void)startAnimatingWithImagesInRange:(NSRange)imageRange duration:(NSTimeInterval)duration repeatCount:(NSInteger)repeatCount {
252+
self.animating = YES;
215253
if (self.animatedImage) {
216-
self.currentStatus.shouldAnimate = YES;
254+
[self startBuiltInAnimation];
217255
} else if (_image.images.count > 0) {
218256
[super startAnimatingWithImagesInRange:imageRange duration:duration repeatCount:repeatCount];
219257
}
220258
}
221259

222260
- (void)stopAnimating {
261+
self.animating = NO;
223262
if (self.animatedImage) {
224-
self.currentStatus.shouldAnimate = NO;
263+
[self stopBuiltInAnimation];
225264
} else if (_image.images.count > 0) {
226265
[super stopAnimating];
227266
}

0 commit comments

Comments
 (0)