mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-17 02:49:57 +00:00
278 lines
9.4 KiB
Objective-C
278 lines
9.4 KiB
Objective-C
//
|
|
// LOTLayerContainer.m
|
|
// Lottie
|
|
//
|
|
// Created by brandon_withrow on 7/18/17.
|
|
// Copyright © 2017 Airbnb. All rights reserved.
|
|
//
|
|
|
|
#import "LOTLayerContainer.h"
|
|
#import "LOTTransformInterpolator.h"
|
|
#import "LOTNumberInterpolator.h"
|
|
#import "CGGeometry+LOTAdditions.h"
|
|
#import "LOTRenderGroup.h"
|
|
#import "LOTHelpers.h"
|
|
#import "LOTMaskContainer.h"
|
|
#import "LOTAsset.h"
|
|
|
|
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
|
|
#import "LOTCacheProvider.h"
|
|
#endif
|
|
|
|
@implementation LOTLayerContainer {
|
|
LOTTransformInterpolator *_transformInterpolator;
|
|
LOTNumberInterpolator *_opacityInterpolator;
|
|
NSNumber *_inFrame;
|
|
NSNumber *_outFrame;
|
|
CALayer *DEBUG_Center;
|
|
LOTRenderGroup *_contentsGroup;
|
|
LOTMaskContainer *_maskLayer;
|
|
NSDictionary *_valueInterpolators;
|
|
}
|
|
|
|
@dynamic currentFrame;
|
|
|
|
- (instancetype)initWithModel:(LOTLayer *)layer
|
|
inLayerGroup:(LOTLayerGroup *)layerGroup {
|
|
self = [super init];
|
|
if (self) {
|
|
_wrapperLayer = [CALayer new];
|
|
[self addSublayer:_wrapperLayer];
|
|
DEBUG_Center = [CALayer layer];
|
|
|
|
DEBUG_Center.bounds = CGRectMake(0, 0, 20, 20);
|
|
DEBUG_Center.borderColor = [UIColor blueColor].CGColor;
|
|
DEBUG_Center.borderWidth = 2;
|
|
DEBUG_Center.masksToBounds = YES;
|
|
|
|
if (ENABLE_DEBUG_SHAPES) {
|
|
[_wrapperLayer addSublayer:DEBUG_Center];
|
|
}
|
|
self.actions = @{@"hidden" : [NSNull null], @"opacity" : [NSNull null], @"transform" : [NSNull null]};
|
|
_wrapperLayer.actions = [self.actions copy];
|
|
[self commonInitializeWith:layer inLayerGroup:layerGroup];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)commonInitializeWith:(LOTLayer *)layer
|
|
inLayerGroup:(LOTLayerGroup *)layerGroup {
|
|
if (layer == nil) {
|
|
return;
|
|
}
|
|
_layerName = layer.layerName;
|
|
if (layer.layerType == LOTLayerTypeImage ||
|
|
layer.layerType == LOTLayerTypeSolid ||
|
|
layer.layerType == LOTLayerTypePrecomp) {
|
|
_wrapperLayer.bounds = CGRectMake(0, 0, layer.layerWidth.floatValue, layer.layerHeight.floatValue);
|
|
_wrapperLayer.anchorPoint = CGPointMake(0, 0);
|
|
_wrapperLayer.masksToBounds = YES;
|
|
DEBUG_Center.position = LOT_RectGetCenterPoint(self.bounds);
|
|
}
|
|
|
|
if (layer.layerType == LOTLayerTypeImage) {
|
|
[self _setImageForAsset:layer.imageAsset];
|
|
}
|
|
|
|
_inFrame = [layer.inFrame copy];
|
|
_outFrame = [layer.outFrame copy];
|
|
_transformInterpolator = [LOTTransformInterpolator transformForLayer:layer];
|
|
if (layer.parentID) {
|
|
NSNumber *parentID = layer.parentID;
|
|
LOTTransformInterpolator *childInterpolator = _transformInterpolator;
|
|
while (parentID != nil) {
|
|
LOTLayer *parentModel = [layerGroup layerModelForID:parentID];
|
|
LOTTransformInterpolator *interpolator = [LOTTransformInterpolator transformForLayer:parentModel];
|
|
childInterpolator.inputNode = interpolator;
|
|
childInterpolator = interpolator;
|
|
parentID = parentModel.parentID;
|
|
}
|
|
}
|
|
_opacityInterpolator = [[LOTNumberInterpolator alloc] initWithKeyframes:layer.opacity.keyframes];
|
|
if (layer.layerType == LOTLayerTypeShape &&
|
|
layer.shapes.count) {
|
|
[self buildContents:layer.shapes];
|
|
}
|
|
if (layer.layerType == LOTLayerTypeSolid) {
|
|
_wrapperLayer.backgroundColor = layer.solidColor.CGColor;
|
|
}
|
|
if (layer.masks.count) {
|
|
_maskLayer = [[LOTMaskContainer alloc] initWithMasks:layer.masks];
|
|
_wrapperLayer.mask = _maskLayer;
|
|
}
|
|
|
|
NSMutableDictionary *interpolators = [NSMutableDictionary dictionary];
|
|
interpolators[@"Transform.Opacity"] = _opacityInterpolator;
|
|
interpolators[@"Transform.Anchor Point"] = _transformInterpolator.anchorInterpolator;
|
|
interpolators[@"Transform.Scale"] = _transformInterpolator.scaleInterpolator;
|
|
interpolators[@"Transform.Rotation"] = _transformInterpolator.scaleInterpolator;
|
|
if (_transformInterpolator.positionXInterpolator &&
|
|
_transformInterpolator.positionYInterpolator) {
|
|
interpolators[@"Transform.X Position"] = _transformInterpolator.positionXInterpolator;
|
|
interpolators[@"Transform.Y Position"] = _transformInterpolator.positionYInterpolator;
|
|
} else if (_transformInterpolator.positionInterpolator) {
|
|
interpolators[@"Transform.Position"] = _transformInterpolator.positionInterpolator;
|
|
}
|
|
_valueInterpolators = interpolators;
|
|
}
|
|
|
|
- (void)buildContents:(NSArray *)contents {
|
|
_contentsGroup = [[LOTRenderGroup alloc] initWithInputNode:nil contents:contents keyname:_layerName];
|
|
[_wrapperLayer addSublayer:_contentsGroup.containerLayer];
|
|
}
|
|
|
|
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
|
|
|
|
- (void)_setImageForAsset:(LOTAsset *)asset {
|
|
if (asset.imageName) {
|
|
UIImage *image;
|
|
if (asset.rootDirectory.length > 0) {
|
|
NSString *rootDirectory = asset.rootDirectory;
|
|
if (asset.imageDirectory.length > 0) {
|
|
rootDirectory = [rootDirectory stringByAppendingPathComponent:asset.imageDirectory];
|
|
}
|
|
NSString *imagePath = [rootDirectory stringByAppendingPathComponent:asset.imageName];
|
|
|
|
id<LOTImageCache> imageCache = [LOTCacheProvider imageCache];
|
|
if (imageCache) {
|
|
image = [imageCache imageForKey:imagePath];
|
|
if (!image) {
|
|
image = [UIImage imageWithContentsOfFile:imagePath];
|
|
[imageCache setImage:image forKey:imagePath];
|
|
}
|
|
} else {
|
|
image = [UIImage imageWithContentsOfFile:imagePath];
|
|
}
|
|
}else{
|
|
NSArray *components = [asset.imageName componentsSeparatedByString:@"."];
|
|
image = [UIImage imageNamed:components.firstObject inBundle:asset.assetBundle compatibleWithTraitCollection:nil];
|
|
}
|
|
|
|
if (image) {
|
|
_wrapperLayer.contents = (__bridge id _Nullable)(image.CGImage);
|
|
} else {
|
|
NSLog(@"%s: Warn: image not found: %@", __PRETTY_FUNCTION__, asset.imageName);
|
|
}
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
- (void)_setImageForAsset:(LOTAsset *)asset {
|
|
if (asset.imageName) {
|
|
NSArray *components = [asset.imageName componentsSeparatedByString:@"."];
|
|
NSImage *image = [NSImage imageNamed:components.firstObject];
|
|
if (image) {
|
|
NSWindow *window = [NSApp mainWindow];
|
|
CGFloat desiredScaleFactor = [window backingScaleFactor];
|
|
CGFloat actualScaleFactor = [image recommendedLayerContentsScale:desiredScaleFactor];
|
|
id layerContents = [image layerContentsForContentsScale:actualScaleFactor];
|
|
_wrapperLayer = layerContents;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// MARK - Animation
|
|
|
|
+ (BOOL)needsDisplayForKey:(NSString *)key {
|
|
if ([key isEqualToString:@"currentFrame"]) {
|
|
return YES;
|
|
}
|
|
return [super needsDisplayForKey:key];
|
|
}
|
|
|
|
-(id<CAAction>)actionForKey:(NSString *)event {
|
|
if([event isEqualToString:@"currentFrame"]) {
|
|
CABasicAnimation *theAnimation = [CABasicAnimation
|
|
animationWithKeyPath:event];
|
|
theAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
|
|
theAnimation.fromValue = [[self presentationLayer] valueForKey:event];
|
|
return theAnimation;
|
|
}
|
|
return [super actionForKey:event];
|
|
}
|
|
|
|
- (id)initWithLayer:(id)layer {
|
|
if (self = [super initWithLayer:layer]) {
|
|
if ([layer isKindOfClass:[LOTLayerContainer class]]) {
|
|
LOTLayerContainer *other = (LOTLayerContainer *)layer;
|
|
self.currentFrame = other.currentFrame;
|
|
}
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)display {
|
|
LOTLayerContainer *presentation = self;
|
|
if (self.animationKeys.count &&
|
|
self.presentationLayer) {
|
|
presentation = (LOTLayerContainer *)self.presentationLayer;
|
|
}
|
|
[self displayWithFrame:presentation.currentFrame];
|
|
}
|
|
|
|
- (void)displayWithFrame:(NSNumber *)frame {
|
|
[self displayWithFrame:frame forceUpdate:NO];
|
|
}
|
|
|
|
- (void)displayWithFrame:(NSNumber *)frame forceUpdate:(BOOL)forceUpdate {
|
|
if (ENABLE_DEBUG_LOGGING) NSLog(@"View %@ Displaying Frame %@", self, frame);
|
|
BOOL hidden = NO;
|
|
if (_inFrame && _outFrame) {
|
|
hidden = (frame.floatValue < _inFrame.floatValue ||
|
|
frame.floatValue > _outFrame.floatValue);
|
|
}
|
|
self.hidden = hidden;
|
|
if (hidden) {
|
|
return;
|
|
}
|
|
if (_opacityInterpolator && [_opacityInterpolator hasUpdateForFrame:frame]) {
|
|
self.opacity = [_opacityInterpolator floatValueForFrame:frame];
|
|
}
|
|
if (_transformInterpolator && [_transformInterpolator hasUpdateForFrame:frame]) {
|
|
_wrapperLayer.transform = [_transformInterpolator transformForFrame:frame];
|
|
}
|
|
[_contentsGroup updateWithFrame:frame withModifierBlock:nil forceLocalUpdate:forceUpdate];
|
|
_maskLayer.currentFrame = frame;
|
|
}
|
|
|
|
- (void)addAndMaskSublayer:(nonnull CALayer *)subLayer {
|
|
[_wrapperLayer addSublayer:subLayer];
|
|
}
|
|
|
|
- (BOOL)setValue:(nonnull id)value
|
|
forKeypath:(nonnull NSString *)keypath
|
|
atFrame:(nullable NSNumber *)frame {
|
|
NSArray *components = [keypath componentsSeparatedByString:@"."];
|
|
NSString *firstKey = components.firstObject;
|
|
if ([firstKey isEqualToString:self.layerName]) {
|
|
NSString *nextPath = [keypath stringByReplacingCharactersInRange:NSMakeRange(0, firstKey.length + 1) withString:@""];
|
|
LOTValueInterpolator *interpolator = _valueInterpolators[nextPath];
|
|
if (interpolator) {
|
|
return [interpolator setValue:value atFrame:frame];
|
|
} else {
|
|
return [_contentsGroup setValue:value forKeyAtPath:keypath forFrame:frame];
|
|
}
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
- (void)setViewportBounds:(CGRect)viewportBounds {
|
|
_viewportBounds = viewportBounds;
|
|
if (_maskLayer) {
|
|
CGPoint center = LOT_RectGetCenterPoint(viewportBounds);
|
|
viewportBounds.origin = CGPointMake(-center.x, -center.y);
|
|
_maskLayer.bounds = viewportBounds;
|
|
}
|
|
}
|
|
|
|
- (void)logHierarchyKeypathsWithParent:(NSString * _Nullable)parent {
|
|
[_contentsGroup logHierarchyKeypathsWithParent:parent
|
|
];
|
|
}
|
|
|
|
@end
|