Merge pull request #2815 from dreampiggy/feature_animated_clear_when_stop

Support to clear frame buffer or reset frame index when stopped for SDAnimatedImageView
This commit is contained in:
DreamPiggy 2019-08-30 21:19:44 +08:00 committed by GitHub
commit 491fefdd48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 116 additions and 10 deletions

View File

@ -58,6 +58,20 @@
*/
@property (nonatomic, assign) BOOL shouldIncrementalLoad;
/**
Whether or not to clear the frame buffer cache when animation stopped. See `maxBufferSize`
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)
Default is NO.
*/
@property (nonatomic, assign) BOOL clearBufferWhenStopped;
/**
Whether or not to reset the current frame index when animation stopped.
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.
Default is NO.
*/
@property (nonatomic, assign) BOOL resetFrameIndexWhenStopped;
#if SD_UIKIT
/**
You can specify a runloop mode to let it rendering.

View File

@ -149,21 +149,15 @@ static NSUInteger SDDeviceFreeMemory() {
self.animatedImage = nil;
self.totalFrameCount = 0;
self.totalLoopCount = 0;
self.currentFrame = nil;
self.currentFrameIndex = 0;
self.currentLoopCount = 0;
self.currentTime = 0;
self.bufferMiss = NO;
// reset current state
[self resetCurrentFrameIndex];
self.shouldAnimate = NO;
self.isProgressive = NO;
self.maxBufferCount = 0;
self.animatedImageScale = 1;
[_fetchQueue cancelAllOperations];
_fetchQueue = nil;
SD_LOCK(self.lock);
[_frameBuffer removeAllObjects];
_frameBuffer = nil;
SD_UNLOCK(self.lock);
// clear buffer cache
[self clearFrameBuffer];
}
- (void)resetProgressiveImage
@ -179,6 +173,22 @@ static NSUInteger SDDeviceFreeMemory() {
// preserve buffer cache
}
- (void)resetCurrentFrameIndex
{
self.currentFrame = nil;
self.currentFrameIndex = 0;
self.currentLoopCount = 0;
self.currentTime = 0;
self.bufferMiss = NO;
}
- (void)clearFrameBuffer
{
SD_LOCK(self.lock);
[_frameBuffer removeAllObjects];
SD_UNLOCK(self.lock);
}
#pragma mark - Accessors
#pragma mark Public
@ -466,6 +476,12 @@ static NSUInteger SDDeviceFreeMemory() {
#else
_displayLink.paused = YES;
#endif
if (self.resetFrameIndexWhenStopped) {
[self resetCurrentFrameIndex];
}
if (self.clearBufferWhenStopped) {
[self clearFrameBuffer];
}
} else {
#if SD_UIKIT
[super stopAnimating];

View File

@ -16,6 +16,7 @@ static const NSUInteger kTestGIFFrameCount = 5; // local TestImage.gif loop coun
@interface SDAnimatedImageView ()
@property (nonatomic, assign) BOOL isProgressive;
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, UIImage *> *frameBuffer;
@end
@ -206,6 +207,7 @@ static const NSUInteger kTestGIFFrameCount = 5; // local TestImage.gif loop coun
#else
imageView.animates = NO;
#endif
[imageView removeFromSuperview];
[expectation fulfill];
}
}];
@ -298,6 +300,80 @@ static const NSUInteger kTestGIFFrameCount = 5; // local TestImage.gif loop coun
[self waitForExpectationsWithCommonTimeout];
}
- (void)test25AnimatedImageStopAnimatingNormal {
XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView stopAnimating normal behavior"];
SDAnimatedImageView *imageView = [SDAnimatedImageView new];
#if SD_UIKIT
[self.window addSubview:imageView];
#else
[self.window.contentView addSubview:imageView];
#endif
// This APNG duration is 2s
SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testAPNGPData]];
imageView.image = image;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 0.5s is not finished, frame index should not be 0
expect(imageView.frameBuffer.count).beGreaterThan(0);
expect(imageView.currentFrameIndex).beGreaterThan(0);
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
#if SD_UIKIT
[imageView stopAnimating];
#else
imageView.animates = NO;
#endif
expect(imageView.frameBuffer.count).beGreaterThan(0);
expect(imageView.currentFrameIndex).beGreaterThan(0);
[imageView removeFromSuperview];
[expectation fulfill];
});
[self waitForExpectationsWithCommonTimeout];
}
- (void)test25AnimatedImageStopAnimatingClearBuffer {
XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView stopAnimating clear buffer when stopped"];
SDAnimatedImageView *imageView = [SDAnimatedImageView new];
imageView.clearBufferWhenStopped = YES;
imageView.resetFrameIndexWhenStopped = YES;
#if SD_UIKIT
[self.window addSubview:imageView];
#else
[self.window.contentView addSubview:imageView];
#endif
// This APNG duration is 2s
SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testAPNGPData]];
imageView.image = image;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 0.5s is not finished, frame index should not be 0
expect(imageView.frameBuffer.count).beGreaterThan(0);
expect(imageView.currentFrameIndex).beGreaterThan(0);
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
#if SD_UIKIT
[imageView stopAnimating];
#else
imageView.animates = NO;
#endif
expect(imageView.frameBuffer.count).equal(0);
expect(imageView.currentFrameIndex).equal(0);
[imageView removeFromSuperview];
[expectation fulfill];
});
[self waitForExpectationsWithCommonTimeout];
}
#pragma mark - Helper
- (UIWindow *)window {
if (!_window) {