8
8
9
9
#import " SDAnimatedImageInterface.h"
10
10
#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
16
11
17
12
#pragma mark - SPI
18
13
@@ -35,6 +30,14 @@ @protocol UIViewProtocol <NSObject>
35
30
36
31
@end
37
32
33
+ @protocol UIImageViewProtocol <NSObject >
34
+
35
+ - (void )startAnimating ;
36
+ - (void )stopAnimating ;
37
+ @property (nonatomic , readonly , getter =isAnimating) BOOL animating;
38
+
39
+ @end
40
+
38
41
@interface WKInterfaceObject ()
39
42
40
43
// This is needed for dynamic created WKInterfaceObject, like `WKInterfaceMap`
@@ -44,40 +47,17 @@ - (instancetype)_initForDynamicCreationWithInterfaceProperty:(NSString *)propert
44
47
45
48
@end
46
49
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
-
66
50
@interface SDAnimatedImageInterface () {
67
51
UIImage *_image;
68
52
}
69
53
70
54
@property (nonatomic , strong , readwrite ) UIImage *currentFrame;
71
55
@property (nonatomic , assign , readwrite ) NSUInteger currentFrameIndex;
72
56
@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;
78
57
@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.
81
61
82
62
@end
83
63
@@ -125,142 +105,113 @@ - (void)setImage:(UIImage *)image {
125
105
_image = image;
126
106
127
107
// 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 ;
131
112
132
113
[super setImage: image];
133
114
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
138
120
return ;
139
121
}
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 ;
155
126
}
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];
156
154
}
157
155
}
158
156
159
157
- (void )updateAnimation {
160
158
[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 ];
177
161
} 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 ];
184
163
}
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;
207
164
}
208
165
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 ;
219
171
}
220
172
}
221
173
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 ];
233
177
}
234
178
235
179
- (void )updateShouldAnimate
236
180
{
237
181
id <UIViewProtocol> view = [self _interfaceView ];
238
182
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
+ }
240
194
}
241
195
242
196
- (void )startAnimating {
243
- self.animating = YES ;
244
- if (self.animatedImage ) {
245
- [self startBuiltInAnimation ];
197
+ if (self.player ) {
198
+ [self .player startPlaying ];
246
199
} else if (_image.images .count > 0 ) {
247
200
[super startAnimating ];
248
201
}
249
202
}
250
203
251
204
- (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 ];
255
207
} else if (_image.images .count > 0 ) {
256
208
[super startAnimatingWithImagesInRange: imageRange duration: duration repeatCount: repeatCount];
257
209
}
258
210
}
259
211
260
212
- (void )stopAnimating {
261
- self.animating = NO ;
262
- if (self.animatedImage ) {
263
- [self stopBuiltInAnimation ];
213
+ if (self.player ) {
214
+ [self .player stopPlaying ];
264
215
} else if (_image.images .count > 0 ) {
265
216
[super stopAnimating ];
266
217
}
0 commit comments