Skip to content

Commit 09f2f74

Browse files
committed
Add the advanced control on SDAnimatedImageView/Player
1 parent 3c9c30c commit 09f2f74

File tree

3 files changed

+109
-24
lines changed

3 files changed

+109
-24
lines changed

SDWebImageSwiftUI/Classes/AnimatedImage.swift

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ public struct AnimatedImage : PlatformViewRepresentable {
6464
var incrementalLoad: Bool?
6565
var maxBufferSize: UInt?
6666
var customLoopCount: Int?
67+
var runLoopMode: RunLoop.Mode?
68+
var pausable: Bool?
69+
var clearBufferWhenStopped: Bool?
70+
var playBackRate: Double?
6771
#if os(macOS) || os(iOS) || os(tvOS)
6872
// These configurations only useful for web image loading
6973
var indicator: SDWebImageIndicator?
@@ -467,12 +471,40 @@ public struct AnimatedImage : PlatformViewRepresentable {
467471
}
468472
#elseif os(watchOS)
469473
if let customLoopCount = self.customLoopCount {
470-
view.wrapped.setAnimationRepeatCount(customLoopCount as NSNumber)
474+
view.wrapped.animationRepeatCount = customLoopCount as NSNumber
471475
} else {
472476
// disable custom loop count
473-
view.wrapped.setAnimationRepeatCount(nil)
477+
view.wrapped.animationRepeatCount = nil
474478
}
475479
#endif
480+
481+
// RunLoop Mode
482+
if let runLoopMode = self.runLoopMode {
483+
view.wrapped.runLoopMode = runLoopMode
484+
} else {
485+
view.wrapped.runLoopMode = .common
486+
}
487+
488+
// Pausable
489+
if let pausable = self.pausable {
490+
view.wrapped.resetFrameIndexWhenStopped = !pausable
491+
} else {
492+
view.wrapped.resetFrameIndexWhenStopped = false
493+
}
494+
495+
// Clear Buffer
496+
if let clearBufferWhenStopped = self.clearBufferWhenStopped {
497+
view.wrapped.clearBufferWhenStopped = clearBufferWhenStopped
498+
} else {
499+
view.wrapped.clearBufferWhenStopped = false
500+
}
501+
502+
// Playback Rate
503+
if let playBackRate = self.playBackRate {
504+
view.wrapped.playbackRate = playBackRate
505+
} else {
506+
view.wrapped.playbackRate = 1.0
507+
}
476508
}
477509
}
478510

@@ -625,6 +657,48 @@ extension AnimatedImage {
625657
result.incrementalLoad = incrementalLoad
626658
return result
627659
}
660+
661+
/// The runLoopMode when animation is playing on. Defaults is `.common`
662+
/// You can specify a runloop mode to let it rendering.
663+
/// - Note: This is useful for some cases, for example, always specify NSDefaultRunLoopMode, if you want to pause the animation when user scroll (for Mac user, drag the mouse or touchpad)
664+
/// - Parameter runLoopMode: The runLoopMode for animation
665+
public func runLoopMode(_ runLoopMode: RunLoop.Mode) -> AnimatedImage {
666+
var result = self
667+
result.runLoopMode = runLoopMode
668+
return result
669+
}
670+
671+
/// Whether or not to pause the animation (keep current frame), instead of stop the animation (frame index reset to 0). When the `isAnimating` binding value changed to false.
672+
/// Defaults is true.
673+
/// - Note: For some of use case, you may want to reset the frame index to 0 when stop, but some other want to keep the current frame index.
674+
/// - Parameter pausable: Whether or not to pause the animation instead of stop the animation.
675+
public func pausable(_ pausable: Bool) -> AnimatedImage {
676+
var result = self
677+
result.pausable = pausable
678+
return result
679+
}
680+
681+
/// Whether or not to clear frame buffer cache when stopped.
682+
/// Note: This is useful when you want to limit the memory usage during frequently visibility changes (such as image view inside a list view, then push and pop)
683+
/// - Parameter clearBufferWhenStopped: Whether or not to clear frame buffer cache when stopped.
684+
public func clearBufferWhenStopped(_ clearBufferWhenStopped: Bool) -> AnimatedImage {
685+
var result = self
686+
result.clearBufferWhenStopped = clearBufferWhenStopped
687+
return result
688+
}
689+
690+
/// Control the animation playback rate. Default is 1.0.
691+
/// `1.0` means the normal speed.
692+
/// `0.0` means stopping the animation.
693+
/// `0.0-1.0` means the slow speed.
694+
/// `> 1.0` means the fast speed.
695+
/// `< 0.0` is not supported currently and stop animation. (may support reverse playback in the future)
696+
/// - Parameter playBackRate: The animation playback rate.
697+
public func playBackRate(_ playBackRate: Double) -> AnimatedImage {
698+
var result = self
699+
result.playBackRate = playBackRate
700+
return result
701+
}
628702
}
629703

630704
// Completion Handler

SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterface.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,17 @@ NS_ASSUME_NONNULL_BEGIN
1515
@interface SDAnimatedImageInterface : WKInterfaceImage
1616

1717
@property (nonatomic, assign, getter=isAnimating, readonly) BOOL animating;
18+
@property (nonatomic, strong, nullable) NSNumber *animationRepeatCount;
19+
@property (nonatomic, copy) NSRunLoopMode runLoopMode;
20+
@property (nonatomic, assign) BOOL resetFrameIndexWhenStopped;
21+
@property (nonatomic, assign) BOOL clearBufferWhenStopped;
22+
@property (nonatomic, assign) double playbackRate;
1823

1924
- (instancetype)init WK_AVAILABLE_WATCHOS_ONLY(6.0);
25+
26+
/// Set the content mode for image
27+
/// @param contentMode The contrent mode
2028
- (void)setContentMode:(SDImageScaleMode)contentMode;
21-
- (void)setAnimationRepeatCount:(nullable NSNumber *)repeatCount;
22-
- (void)setRunLoopMode:(nonnull NSRunLoopMode)runLoopMode;
23-
- (void)setPlaybackRate:(double)playbackRate;
2429

2530
/// Trigger the animation check when view appears/disappears
2631
- (void)updateAnimation;

SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterface.m

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,9 @@ @interface SDAnimatedImageInterface () {
5454
@property (nonatomic, strong, readwrite) UIImage *currentFrame;
5555
@property (nonatomic, assign, readwrite) NSUInteger currentFrameIndex;
5656
@property (nonatomic, assign, readwrite) NSUInteger currentLoopCount;
57-
@property (nonatomic, strong) NSNumber *animationRepeatCount;
57+
@property (nonatomic, assign, getter=isAnimating, readwrite) BOOL animating;
5858
@property (nonatomic, assign) BOOL shouldAnimate;
59-
@property (nonatomic, copy) NSRunLoopMode runLoopMode;
60-
@property (nonatomic, assign) double playbackRate;
61-
@property (nonatomic,strong) SDAnimatedImagePlayer *player; // The animation player.
59+
@property (nonatomic, strong) SDAnimatedImagePlayer *player; // The animation player.
6260
@property (nonatomic) id<CALayerProtocol> imageViewLayer; // The actual rendering layer.
6361

6462
@end
@@ -162,10 +160,18 @@ - (void)setImage:(UIImage *)image {
162160

163161
- (void)updateAnimation {
164162
[self updateShouldAnimate];
165-
if (self.shouldAnimate) {
166-
[self startAnimating];
167-
} else {
168-
[self stopAnimating];
163+
if (!self.player) {
164+
return;
165+
}
166+
// Filter automatically animating changes
167+
if (self.shouldAnimate && self.isAnimating) {
168+
[self.player startPlaying];
169+
} else if (!self.shouldAnimate && !self.isAnimating) {
170+
if (self.resetFrameIndexWhenStopped) {
171+
[self.player stopPlaying];
172+
} else {
173+
[self.player pausePlaying];
174+
}
169175
}
170176
}
171177

@@ -189,17 +195,8 @@ - (void)updateShouldAnimate
189195
self.shouldAnimate = self.player && isVisible;
190196
}
191197

192-
- (BOOL)isAnimating
193-
{
194-
if (self.player) {
195-
return self.player.isPlaying;
196-
} else {
197-
id<UIImageViewProtocol> view = (id<UIImageViewProtocol>)[self _interfaceView];
198-
return [view isAnimating];
199-
}
200-
}
201-
202198
- (void)startAnimating {
199+
self.animating = YES;
203200
if (self.player) {
204201
[self.player startPlaying];
205202
} else if (_image.images.count > 0) {
@@ -208,6 +205,7 @@ - (void)startAnimating {
208205
}
209206

210207
- (void)startAnimatingWithImagesInRange:(NSRange)imageRange duration:(NSTimeInterval)duration repeatCount:(NSInteger)repeatCount {
208+
self.animating = YES;
211209
if (self.player) {
212210
[self.player startPlaying];
213211
} else if (_image.images.count > 0) {
@@ -216,8 +214,16 @@ - (void)startAnimatingWithImagesInRange:(NSRange)imageRange duration:(NSTimeInte
216214
}
217215

218216
- (void)stopAnimating {
217+
self.animating = NO;
219218
if (self.player) {
220-
[self.player stopPlaying];
219+
if (self.resetFrameIndexWhenStopped) {
220+
[self.player stopPlaying];
221+
} else {
222+
[self.player pausePlaying];
223+
}
224+
if (self.clearBufferWhenStopped) {
225+
[self.player clearFrameBuffer];
226+
}
221227
} else if (_image.images.count > 0) {
222228
[super stopAnimating];
223229
}

0 commit comments

Comments
 (0)