SDWebImageSwiftUI/SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterface.m

215 lines
6.2 KiB
Objective-C

/*
* This file is part of the SDWebImage package.
* (c) DreamPiggy <lizhuoli1126@126.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDAnimatedImageInterface.h"
#if SD_WATCH
#pragma mark - SPI
@protocol CALayerProtocol <NSObject>
@property (nullable, strong) id contents;
@property CGFloat contentsScale;
@end
@protocol UIViewProtocol <NSObject>
@property (nonatomic, strong, readonly) id<CALayerProtocol> layer;
@property (nonatomic, assign) SDImageScaleMode contentMode;
@property (nonatomic, readonly) id<UIViewProtocol> superview;
@property (nonatomic, readonly, copy) NSArray<id<UIViewProtocol>> *subviews;
@property (nonatomic, readonly) id window;
@property (nonatomic) CGFloat alpha;
@property (nonatomic, getter=isHidden) BOOL hidden;
@property (nonatomic, getter=isOpaque) BOOL opaque;
@property (nonatomic) CGRect frame;
@property (nonatomic) CGRect bounds;
@property (nonatomic) CGPoint center;
@property (nonatomic, readonly) CGSize intrinsicContentSize;
@property(nonatomic) NSInteger tag;
- (void)invalidateIntrinsicContentSize;
- (void)layoutSubviews;
- (CGSize)sizeThatFits:(CGSize)size;
- (void)sizeToFit;
@end
@protocol UIImageViewProtocol <UIViewProtocol>
@property (nullable, nonatomic, strong) UIImage *image;
- (void)startAnimating;
- (void)stopAnimating;
@property (nonatomic, readonly, getter=isAnimating) BOOL animating;
@end
@interface WKInterfaceObject ()
// This is needed for dynamic created WKInterfaceObject, like `WKInterfaceMap`
- (instancetype)_initForDynamicCreationWithInterfaceProperty:(NSString *)property;
- (NSDictionary *)interfaceDescriptionForDynamicCreation;
// This is remote UIView
@property (nonatomic, strong, readwrite) id<UIViewProtocol> _interfaceView;
@end
@interface SDAnimatedImageInterface ()
@property (nonatomic, strong, readwrite) UIImage *currentFrame;
@property (nonatomic, assign, readwrite) NSUInteger currentFrameIndex;
@property (nonatomic, assign, readwrite) NSUInteger currentLoopCount;
@property (nonatomic, assign, getter=isAnimating, readwrite) BOOL animating;
@property (nonatomic, assign) BOOL shouldAnimate;
@property (nonatomic, strong) SDAnimatedImagePlayer *player; // The animation player.
@property (nonatomic) id<CALayerProtocol> imageViewLayer; // The actual rendering layer.
@end
@implementation SDAnimatedImageInterface
- (instancetype)init {
Class cls = [self class];
NSString *UUID = [NSUUID UUID].UUIDString;
NSString *property = [NSString stringWithFormat:@"%@_%@", cls, UUID];
self = [self _initForDynamicCreationWithInterfaceProperty:property];
if (self) {
self.runLoopMode = NSRunLoopCommonModes;
self.playbackRate = 1.0;
}
return self;
}
- (NSDictionary *)interfaceDescriptionForDynamicCreation {
// This is called by WatchKit
return @{
@"type" : @"image",
@"property" : self.interfaceProperty,
};
}
- (void)setImage:(UIImage *)image {
if (_image == image) {
return;
}
_image = image;
// Stop animating
self.player = nil;
self.currentFrame = nil;
self.currentFrameIndex = 0;
self.currentLoopCount = 0;
((id<UIImageViewProtocol>)[self _interfaceView]).image = image;
if ([image.class conformsToProtocol:@protocol(SDAnimatedImage)]) {
// Create animted player
self.player = [SDAnimatedImagePlayer playerWithProvider:(id<SDAnimatedImage>)image];
if (!self.player) {
// animated player nil means the image format is not supported, or frame count <= 1
return;
}
// Custom Loop Count
if (self.animationRepeatCount != nil) {
self.player.totalLoopCount = self.animationRepeatCount.unsignedIntegerValue;
}
// RunLoop Mode
self.player.runLoopMode = self.runLoopMode;
// Play Rate
self.player.playbackRate = self.playbackRate;
// Setup handler
__weak typeof(self) wself = self;
self.player.animationFrameHandler = ^(NSUInteger index, UIImage * frame) {
__strong typeof(self) sself = wself;
sself.currentFrameIndex = index;
sself.currentFrame = frame;
[sself displayLayer:sself.imageViewLayer];
};
self.player.animationLoopHandler = ^(NSUInteger loopCount) {
__strong typeof(self) sself = wself;
sself.currentLoopCount = loopCount;
};
// Start animating
[self startAnimating];
[self displayLayer:self.imageViewLayer];
}
}
- (void)updateAnimation {
[self updateShouldAnimate];
if (self.shouldAnimate && self.isAnimating) {
[self startAnimating];
} else {
[self stopAnimating];
}
}
- (void)displayLayer:(id<CALayerProtocol>)layer {
UIImage *currentFrame = self.currentFrame;
if (currentFrame) {
layer.contentsScale = currentFrame.scale;
layer.contents = (__bridge id)currentFrame.CGImage;
}
}
// on watchOS, it's the native imageView itself's layer
- (id<CALayerProtocol>)imageViewLayer {
return [self _interfaceView].layer;
}
- (void)updateShouldAnimate
{
id<UIViewProtocol> view = [self _interfaceView];
BOOL isVisible = view.window && view.superview && ![view isHidden] && view.alpha > 0.0;
self.shouldAnimate = self.player && isVisible;
}
- (void)startAnimating {
self.animating = YES;
if (self.player) {
[self updateShouldAnimate];
if (self.shouldAnimate) {
[self.player startPlaying];
}
} else if (_image.images.count > 0) {
[super startAnimating];
}
}
- (void)stopAnimating {
self.animating = NO;
if (self.player) {
if (self.resetFrameIndexWhenStopped) {
[self.player stopPlaying];
} else {
[self.player pausePlaying];
}
if (self.clearBufferWhenStopped) {
[self.player clearFrameBuffer];
}
} else if (_image.images.count > 0) {
[super stopAnimating];
}
}
- (void)setContentMode:(SDImageScaleMode)contentMode {
[self _interfaceView].contentMode = contentMode;
}
- (SDImageScaleMode)contentMode {
return [self _interfaceView].contentMode;
}
@end
#endif