88
99#import " SDAnimatedImageInterface.h"
1010#if SD_WATCH
11- // ImageIO.modulemap does not contains this public header
12- #pragma clang diagnostic push
13- #pragma clang diagnostic ignored "-Wincomplete-umbrella"
14- #import < ImageIO/CGImageAnimation.h>
15- #pragma clang diagnostic pop
1611
1712#pragma mark - SPI
1813
@@ -35,6 +30,14 @@ @protocol UIViewProtocol <NSObject>
3530
3631@end
3732
33+ @protocol UIImageViewProtocol <NSObject >
34+
35+ - (void )startAnimating ;
36+ - (void )stopAnimating ;
37+ @property (nonatomic , readonly , getter =isAnimating) BOOL animating;
38+
39+ @end
40+
3841@interface WKInterfaceObject ()
3942
4043// This is needed for dynamic created WKInterfaceObject, like `WKInterfaceMap`
@@ -44,40 +47,17 @@ - (instancetype)_initForDynamicCreationWithInterfaceProperty:(NSString *)propert
4447
4548@end
4649
47- @interface SDAnimatedImageStatus : NSObject
48-
49- @property (nonatomic , assign ) BOOL shouldAnimate;
50- @property (nonatomic , assign ) CGImageAnimationStatus animationStatus;
51-
52- @end
53-
54- @implementation SDAnimatedImageStatus
55-
56- - (instancetype )init {
57- self = [super init ];
58- if (self) {
59- _animationStatus = kCGImageAnimationStatus_Uninitialized ;
60- }
61- return self;
62- }
63-
64- @end
65-
6650@interface SDAnimatedImageInterface () {
6751 UIImage *_image;
6852}
6953
7054@property (nonatomic , strong , readwrite ) UIImage *currentFrame;
7155@property (nonatomic , assign , readwrite ) NSUInteger currentFrameIndex;
7256@property (nonatomic , assign , readwrite ) NSUInteger currentLoopCount;
73- @property (nonatomic , assign ) NSUInteger totalFrameCount;
74- @property (nonatomic , assign ) NSUInteger totalLoopCount;
75- @property (nonatomic , strong ) UIImage<SDAnimatedImage> *animatedImage;
76- @property (nonatomic , assign ) CGFloat animatedImageScale;
77- @property (nonatomic , strong ) SDAnimatedImageStatus *currentStatus;
7857@property (nonatomic , strong ) NSNumber *animationRepeatCount;
79- @property (nonatomic , assign , getter =isAnimatedFormat) BOOL animatedFormat;
80- @property (nonatomic , assign , getter =isAnimating) BOOL animating;
58+ @property (nonatomic , assign ) BOOL shouldAnimate;
59+ @property (nonatomic ,strong ) SDAnimatedImagePlayer *player; // The animation player.
60+ @property (nonatomic ) id <CALayerProtocol> imageViewLayer; // The actual rendering layer.
8161
8262@end
8363
@@ -125,142 +105,113 @@ - (void)setImage:(UIImage *)image {
125105 _image = image;
126106
127107 // Stop animating
128- [self stopBuiltInAnimation ];
129- // Reset all value
130- [self resetAnimatedImage ];
108+ self.player = nil ;
109+ self.currentFrame = nil ;
110+ self.currentFrameIndex = 0 ;
111+ self.currentLoopCount = 0 ;
131112
132113 [super setImage: image];
133114 if ([image.class conformsToProtocol: @protocol (SDAnimatedImage)]) {
134- UIImage<SDAnimatedImage> *animatedImage = (UIImage<SDAnimatedImage> *)image;
135- NSUInteger animatedImageFrameCount = animatedImage.animatedImageFrameCount ;
136- // Check the frame count
137- if (animatedImageFrameCount <= 1 ) {
115+ // Create animted player
116+ self.player = [SDAnimatedImagePlayer playerWithProvider: (id <SDAnimatedImage>)image];
117+
118+ if (!self.player ) {
119+ // animated player nil means the image format is not supported, or frame count <= 1
138120 return ;
139121 }
140- self.animatedImage = animatedImage;
141- self.totalFrameCount = animatedImageFrameCount;
142- // Get the current frame and loop count.
143- self.totalLoopCount = self.animatedImage .animatedImageLoopCount ;
144- // Get the scale
145- self.animatedImageScale = image.scale ;
146-
147- NSData *animatedImageData = animatedImage.animatedImageData ;
148- SDImageFormat format = [NSData sd_imageFormatForImageData: animatedImageData];
149- if (format == SDImageFormatGIF || format == SDImageFormatPNG) {
150- self.animatedFormat = YES ;
151- [self startBuiltInAnimation ];
152- } else {
153- self.animatedFormat = NO ;
154- [self stopBuiltInAnimation ];
122+
123+ // Custom Loop Count
124+ if (self.animationRepeatCount != nil ) {
125+ self.player .totalLoopCount = self.animationRepeatCount .unsignedIntegerValue ;
155126 }
127+
128+ // // RunLoop Mode
129+ // self.player.runLoopMode = self.runLoopMode;
130+ //
131+ // // Play Rate
132+ // self.player.playbackRate = self.playbackRate;
133+
134+ // Setup handler
135+ __weak typeof (self) wself = self;
136+ self.player .animationFrameHandler = ^(NSUInteger index, UIImage * frame) {
137+ __strong typeof (self) sself = wself;
138+ sself.currentFrameIndex = index;
139+ sself.currentFrame = frame;
140+ [sself displayLayer: sself.imageViewLayer];
141+ };
142+ self.player .animationLoopHandler = ^(NSUInteger loopCount) {
143+ __strong typeof (self) sself = wself;
144+ sself.currentLoopCount = loopCount;
145+ };
146+
147+ // Update should animate
148+ [self updateShouldAnimate ];
149+ if (self.shouldAnimate ) {
150+ [self startAnimating ];
151+ }
152+
153+ [self displayLayer: self .imageViewLayer];
156154 }
157155}
158156
159157- (void )updateAnimation {
160158 [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 ;
173- NSData *animatedImageData = animatedImage.animatedImageData ;
174- NSUInteger maxLoopCount;
175- if (self.animationRepeatCount != nil ) {
176- maxLoopCount = self.animationRepeatCount .unsignedIntegerValue ;
159+ if (self.shouldAnimate ) {
160+ [self startAnimating ];
177161 } else {
178- maxLoopCount = animatedImage.animatedImageLoopCount ;
179- }
180- if (maxLoopCount == 0 ) {
181- // The documentation says `kCFNumberPositiveInfinity may be used`, but it actually treat as 1 loop count
182- // 0 was treated as 1 loop count as well, not the same as Image/IO or UIKit
183- maxLoopCount = ((__bridge NSNumber *)kCFNumberPositiveInfinity ).unsignedIntegerValue - 1 ;
162+ [self stopAnimating ];
184163 }
185- NSDictionary *options = @{(__bridge NSString *)kCGImageAnimationLoopCount : @(maxLoopCount)};
186- SDAnimatedImageStatus *status = [[SDAnimatedImageStatus alloc ] init ];
187- status.shouldAnimate = YES ;
188- __weak typeof (self) wself = self;
189- status.animationStatus = CGAnimateImageDataWithBlock ((__bridge CFDataRef)animatedImageData, (__bridge CFDictionaryRef)options, ^(size_t index, CGImageRef _Nonnull imageRef, bool * _Nonnull stop) {
190- __strong typeof (wself) self = wself;
191- if (!self) {
192- *stop = YES ;
193- return ;
194- }
195- if (!status.shouldAnimate ) {
196- *stop = YES ;
197- return ;
198- }
199- // The CGImageRef provided by this API is GET only, should not call CGImageRelease
200- self.currentFrame = [[UIImage alloc ] initWithCGImage: imageRef scale: self .animatedImageScale orientation: UIImageOrientationUp];
201- self.currentFrameIndex = index;
202- // Render the frame
203- [self displayLayer ];
204- });
205-
206- self.currentStatus = status;
207164}
208165
209- - (void )stopBuiltInAnimation {
210- self.currentStatus .shouldAnimate = NO ;
211- self.currentStatus .animationStatus = kCGImageAnimationStatus_Uninitialized ;
212- }
213-
214- - (void )displayLayer {
215- if (self.currentFrame ) {
216- id <CALayerProtocol> layer = [self _interfaceView ].layer ;
217- layer.contentsScale = self.animatedImageScale ;
218- layer.contents = (__bridge id )self.currentFrame .CGImage ;
166+ - (void )displayLayer : (id <CALayerProtocol>)layer {
167+ UIImage *currentFrame = self.currentFrame ;
168+ if (currentFrame) {
169+ layer.contentsScale = currentFrame.scale ;
170+ layer.contents = (__bridge id )currentFrame.CGImage ;
219171 }
220172}
221173
222- - (void )resetAnimatedImage
223- {
224- self.animatedImage = nil ;
225- self.totalFrameCount = 0 ;
226- self.totalLoopCount = 0 ;
227- self.currentFrame = nil ;
228- self.currentFrameIndex = 0 ;
229- self.currentLoopCount = 0 ;
230- self.animatedImageScale = 1 ;
231- self.animatedFormat = NO ;
232- self.currentStatus = nil ;
174+ // on watchOS, it's the native imageView itself's layer
175+ - (id <CALayerProtocol>)imageViewLayer {
176+ return [[self _interfaceView ] layer ];
233177}
234178
235179- (void )updateShouldAnimate
236180{
237181 id <UIViewProtocol> view = [self _interfaceView ];
238182 BOOL isVisible = view.window && view.superview && ![view isHidden ] && view.alpha > 0.0 ;
239- self.currentStatus .shouldAnimate = self.isAnimating && self.animatedImage && self.isAnimatedFormat && self.totalFrameCount > 1 && isVisible;
183+ self.shouldAnimate = self.player && isVisible;
184+ }
185+
186+ - (BOOL )isAnimating
187+ {
188+ if (self.player ) {
189+ return self.player .isPlaying ;
190+ } else {
191+ id <UIImageViewProtocol> view = (id <UIImageViewProtocol>)[self _interfaceView ];
192+ return [view isAnimating ];
193+ }
240194}
241195
242196- (void )startAnimating {
243- self.animating = YES ;
244- if (self.animatedImage ) {
245- [self startBuiltInAnimation ];
197+ if (self.player ) {
198+ [self .player startPlaying ];
246199 } else if (_image.images .count > 0 ) {
247200 [super startAnimating ];
248201 }
249202}
250203
251204- (void )startAnimatingWithImagesInRange : (NSRange )imageRange duration : (NSTimeInterval )duration repeatCount : (NSInteger )repeatCount {
252- self.animating = YES ;
253- if (self.animatedImage ) {
254- [self startBuiltInAnimation ];
205+ if (self.player ) {
206+ [self .player startPlaying ];
255207 } else if (_image.images .count > 0 ) {
256208 [super startAnimatingWithImagesInRange: imageRange duration: duration repeatCount: repeatCount];
257209 }
258210}
259211
260212- (void )stopAnimating {
261- self.animating = NO ;
262- if (self.animatedImage ) {
263- [self stopBuiltInAnimation ];
213+ if (self.player ) {
214+ [self .player stopPlaying ];
264215 } else if (_image.images .count > 0 ) {
265216 [super stopAnimating ];
266217 }
0 commit comments