diff --git a/Example/Podfile.lock b/Example/Podfile.lock index b6fe57e7cf..2daafa397b 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - lottie-ios (2.0.2) + - lottie-ios (2.0.3) DEPENDENCIES: - lottie-ios (from `../`) @@ -9,7 +9,7 @@ EXTERNAL SOURCES: :path: "../" SPEC CHECKSUMS: - lottie-ios: c9ffa6f63af639e79eb45087c417974cb4435956 + lottie-ios: 445f77c7f661f95587e49095a1ac7de43f0da896 PODFILE CHECKSUM: fdbd59f361db8744871f0e9a0b3f94e0b7b8ca6b diff --git a/Example/Pods/Local Podspecs/lottie-ios.podspec.json b/Example/Pods/Local Podspecs/lottie-ios.podspec.json index 160f00e613..d5d4b0f55b 100644 --- a/Example/Pods/Local Podspecs/lottie-ios.podspec.json +++ b/Example/Pods/Local Podspecs/lottie-ios.podspec.json @@ -1,6 +1,6 @@ { "name": "lottie-ios", - "version": "2.0.2", + "version": "2.0.3", "summary": "Used to natively render vector animations exported from After Effects.", "description": "Lottie is a mobile library for Android and iOS that parses Adobe After Effects animations exported as json with bodymovin and renders the vector animations natively on mobile and through React Native!\n\nFor the first time, designers can create and ship beautiful animations without an enginineer painstakingly recreating it be hand. Since the animation is backed by JSON they are extremely small in size but can be large in complexity! Animations can be played, resized, looped, sped up, slowed down, and even interactively scrubbed.", "homepage": "https://github.com/airbnb/lottie-ios", @@ -13,7 +13,7 @@ }, "source": { "git": "https://github.com/airbnb/lottie-ios.git", - "tag": "2.0.2" + "tag": "2.0.3" }, "platforms": { "ios": "8.0", diff --git a/Example/Pods/Manifest.lock b/Example/Pods/Manifest.lock index b6fe57e7cf..2daafa397b 100644 --- a/Example/Pods/Manifest.lock +++ b/Example/Pods/Manifest.lock @@ -1,5 +1,5 @@ PODS: - - lottie-ios (2.0.2) + - lottie-ios (2.0.3) DEPENDENCIES: - lottie-ios (from `../`) @@ -9,7 +9,7 @@ EXTERNAL SOURCES: :path: "../" SPEC CHECKSUMS: - lottie-ios: c9ffa6f63af639e79eb45087c417974cb4435956 + lottie-ios: 445f77c7f661f95587e49095a1ac7de43f0da896 PODFILE CHECKSUM: fdbd59f361db8744871f0e9a0b3f94e0b7b8ca6b diff --git a/Example/Pods/Target Support Files/lottie-ios-OSX/Info.plist b/Example/Pods/Target Support Files/lottie-ios-OSX/Info.plist index 09cb0fc54c..763f9a650a 100644 --- a/Example/Pods/Target Support Files/lottie-ios-OSX/Info.plist +++ b/Example/Pods/Target Support Files/lottie-ios-OSX/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.0.2 + 2.0.3 CFBundleSignature ???? CFBundleVersion diff --git a/Example/Pods/Target Support Files/lottie-ios-iOS/Info.plist b/Example/Pods/Target Support Files/lottie-ios-iOS/Info.plist index 09cb0fc54c..763f9a650a 100644 --- a/Example/Pods/Target Support Files/lottie-ios-iOS/Info.plist +++ b/Example/Pods/Target Support Files/lottie-ios-iOS/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.0.2 + 2.0.3 CFBundleSignature ???? CFBundleVersion diff --git a/Example/lottie-ios.xcodeproj/project.pbxproj b/Example/lottie-ios.xcodeproj/project.pbxproj index 0c9656f011..8faf1964c7 100644 --- a/Example/lottie-ios.xcodeproj/project.pbxproj +++ b/Example/lottie-ios.xcodeproj/project.pbxproj @@ -83,7 +83,6 @@ FA1F5A951E42B25500FF36BF /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FA1F5A941E42B25500FF36BF /* ViewController.m */; }; FA1F5A971E42B25500FF36BF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FA1F5A961E42B25500FF36BF /* Assets.xcassets */; }; FA1F5A9A1E42B25500FF36BF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FA1F5A981E42B25500FF36BF /* Main.storyboard */; }; - FAFA304C27E35A5B877B245D /* Pods_Lottie_Example_MacOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AC7587B24F90B40ADD0CEA8C /* Pods_Lottie_Example_MacOS.framework */; }; FC6DFE4EE48E30E0517FE2B2 /* Pods_Lottie_Viewer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CED53248BEFE3C87F0FE12C7 /* Pods_Lottie_Viewer.framework */; }; /* End PBXBuildFile section */ @@ -233,7 +232,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - FAFA304C27E35A5B877B245D /* Pods_Lottie_Example_MacOS.framework in Frameworks */, FC6DFE4EE48E30E0517FE2B2 /* Pods_Lottie_Viewer.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/lottie-ios/Classes/Private/LOTAnimationView.m b/lottie-ios/Classes/Private/LOTAnimationView.m index f7b226be58..f43ab7c267 100644 --- a/lottie-ios/Classes/Private/LOTAnimationView.m +++ b/lottie-ios/Classes/Private/LOTAnimationView.m @@ -17,10 +17,14 @@ @implementation LOTAnimationView { CABasicAnimation *_playAnimation; LOTCompositionContainer *_compContainer; + NSNumber *_playRangeStartFrame; + NSNumber *_playRangeEndFrame; + CGFloat _playRangeStartProgress; + CGFloat _playRangeEndProgress; NSBundle *_bundle; } -# pragma mark - Initializers +# pragma mark - Convenience Initializers + (nonnull instancetype)animationNamed:(nonnull NSString *)animationName { return [self animationNamed:animationName inBundle:[NSBundle mainBundle]]; @@ -85,6 +89,8 @@ + (nonnull instancetype)animationWithFilePath:(nonnull NSString *)filePath { return [[LOTAnimationView alloc] initWithModel:nil inBundle:nil]; } +# pragma mark - Initializers + - (instancetype)initWithContentsOfURL:(NSURL *)url { self = [super initWithFrame:CGRectZero]; if (self) { @@ -152,11 +158,10 @@ - (void)_commonInit { _animationProgress = 0; _loopAnimation = NO; _autoReverseAnimation = NO; -} - -- (void)setSceneModel:(LOTComposition *)sceneModel { - _sceneModel = sceneModel; - [self _setupWithSceneModel:sceneModel]; + _playRangeEndFrame = nil; + _playRangeStartFrame = nil; + _playRangeEndProgress = 0; + _playRangeStartProgress = 0; } - (void)_setupWithSceneModel:(LOTComposition *)model { @@ -182,19 +187,71 @@ - (void)_setupWithSceneModel:(LOTComposition *)model { - (void)_restoreState { if (_isAnimationPlaying) { _isAnimationPlaying = NO; - [self playWithCompletion:self.completionBlock]; + if (_playRangeStartFrame && _playRangeEndProgress) { + [self playFromFrame:_playRangeStartFrame toFrame:_playRangeEndFrame withCompletion:self.completionBlock]; + } else if (_playRangeEndProgress != _playRangeStartProgress) { + [self playFromProgress:_playRangeStartProgress toProgress:_playRangeEndProgress withCompletion:self.completionBlock]; + } else { + [self playWithCompletion:self.completionBlock]; + } } else { self.animationProgress = _animationProgress; } } -# pragma mark - External Methods +- (void)_removeCurrentAnimationIfNecessary { + _playAnimation.speed = 0; + _isAnimationPlaying = NO; + _playAnimation.delegate = nil; + [_compContainer removeAllAnimations]; + _playAnimation = nil; +} + +- (CGFloat)_progressForFrame:(NSNumber *)frame { + if (!_sceneModel) { + return 0; + } + return ((frame.floatValue - _sceneModel.startFrame.floatValue) / (_sceneModel.endFrame.floatValue - _sceneModel.startFrame.floatValue)); +} + +- (NSNumber *)_frameForProgress:(CGFloat)progress { + if (!_sceneModel) { + return @0; + } + return @(((_sceneModel.endFrame.floatValue - _sceneModel.startFrame.floatValue) * progress) + _sceneModel.startFrame.floatValue); +} + +# pragma mark - Completion Block + +- (void)_callCompletionIfNecessary:(BOOL)complete { + if (self.completionBlock) { + self.completionBlock(complete); + self.completionBlock = nil; + } +} + +# pragma mark - External Methods - Model + +- (void)setSceneModel:(LOTComposition *)sceneModel { + [self _setupWithSceneModel:sceneModel]; +} + +# pragma mark - External Methods - Play Control - (void)play { + if (!_sceneModel) { + _isAnimationPlaying = YES; + return; + } [self playFromFrame:_sceneModel.startFrame toFrame:_sceneModel.endFrame withCompletion:nil]; } - (void)playWithCompletion:(LOTAnimationCompletionBlock)completion { + if (!_sceneModel) { + _isAnimationPlaying = YES; + self.completionBlock = completion; + return; + } [self playFromFrame:_sceneModel.startFrame toFrame:_sceneModel.endFrame withCompletion:completion]; } @@ -204,10 +261,17 @@ - (void)playToProgress:(CGFloat)progress withCompletion:(nullable LOTAnimationCo - (void)playFromProgress:(CGFloat)fromStartProgress toProgress:(CGFloat)toEndProgress - withCompletion:(nullable LOTAnimationCompletionBlock)completion{ - CGFloat startFrame = ((_sceneModel.endFrame.floatValue - _sceneModel.startFrame.floatValue) * fromStartProgress) + _sceneModel.startFrame.floatValue; - CGFloat endFrame = ((_sceneModel.endFrame.floatValue - _sceneModel.startFrame.floatValue) * toEndProgress) + _sceneModel.startFrame.floatValue; - [self playFromFrame:@(startFrame) toFrame:@(endFrame) withCompletion:completion]; + withCompletion:(nullable LOTAnimationCompletionBlock)completion { + if (!_sceneModel) { + _isAnimationPlaying = YES; + self.completionBlock = completion; + _playRangeStartProgress = fromStartProgress; + _playRangeEndProgress = toEndProgress; + return; + } + [self playFromFrame:[self _frameForProgress:fromStartProgress] + toFrame:[self _frameForProgress:toEndProgress] + withCompletion:completion]; } - (void)playToFrame:(nonnull NSNumber *)toFrame @@ -221,13 +285,23 @@ - (void)playFromFrame:(nonnull NSNumber *)fromStartFrame if (_isAnimationPlaying) { return; } + _playRangeStartFrame = fromStartFrame; + _playRangeEndFrame = toEndFrame; + if (completion) { + self.completionBlock = completion; + } if (!_sceneModel) { _isAnimationPlaying = YES; return; } - if (_animationProgress == 1) { - _animationProgress = 0; + NSNumber *currentFrame = [self _frameForProgress:_animationProgress]; + + currentFrame = @(MAX(MIN(currentFrame.floatValue, toEndFrame.floatValue), fromStartFrame.floatValue)); + if (currentFrame.floatValue == toEndFrame.floatValue) { + currentFrame = fromStartFrame; } + _animationProgress = [self _progressForFrame:currentFrame]; + NSTimeInterval offset = MAX(0, (_animationProgress * (_sceneModel.endFrame.floatValue - _sceneModel.startFrame.floatValue)) - fromStartFrame.floatValue) / _sceneModel.framerate.floatValue; NSTimeInterval duration = ((toEndFrame.floatValue - fromStartFrame.floatValue) / _sceneModel.framerate.floatValue); CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"currentFrame"]; @@ -240,66 +314,81 @@ - (void)playFromFrame:(nonnull NSNumber *)fromStartFrame animation.autoreverses = _autoReverseAnimation; animation.delegate = self; animation.removedOnCompletion = NO; - if (completion) { - self.completionBlock = completion; - } _playAnimation = animation; _playAnimation.beginTime = CACurrentMediaTime() - offset; [_compContainer addAnimation:animation forKey:@"play"]; _isAnimationPlaying = YES; } +#pragma mark - Other Time Controls + - (void)stop { - self.animationProgress = 0; + if (!_sceneModel || + !_isAnimationPlaying) { + _isAnimationPlaying = NO; + return; + } + [self setProgressWithFrame:_sceneModel.startFrame callCompletionIfNecessary:YES]; } - (void)pause { - if (!_sceneModel) { + if (!_sceneModel || + !_isAnimationPlaying) { _isAnimationPlaying = NO; return; } - _playAnimation.delegate = nil; - _playAnimation.speed = 0; NSNumber *frame = [_compContainer.presentationLayer.currentFrame copy]; - _animationProgress = frame.floatValue / _sceneModel.endFrame.floatValue; + [self setProgressWithFrame:frame callCompletionIfNecessary:YES]; +} + +- (void)setAnimationProgress:(CGFloat)animationProgress { + if (!_sceneModel) { + _animationProgress = animationProgress; + return; + } + [self setProgressWithFrame:[self _frameForProgress:animationProgress] callCompletionIfNecessary:YES]; +} + +- (void)setProgressWithFrame:(nonnull NSNumber *)currentFrame { + [self setProgressWithFrame:currentFrame callCompletionIfNecessary:YES]; +} + +- (void)setProgressWithFrame:(nonnull NSNumber *)currentFrame callCompletionIfNecessary:(BOOL)callCompletion { [self _removeCurrentAnimationIfNecessary]; - [self _callCompletionIfNecessary:NO]; + + _animationProgress = [self _progressForFrame:currentFrame]; + [CATransaction begin]; [CATransaction setDisableActions:YES]; - _compContainer.currentFrame = frame; + _compContainer.currentFrame = currentFrame; + [_compContainer setNeedsDisplay]; [CATransaction commit]; + if (callCompletion) { + [self _callCompletionIfNecessary:NO]; + } } - (void)setLoopAnimation:(BOOL)loopAnimation { _loopAnimation = loopAnimation; if (_isAnimationPlaying && _sceneModel) { - - NSNumber *frame = [(LOTCompositionContainer *)_compContainer.presentationLayer currentFrame]; - NSNumber *start = _playAnimation.fromValue; - NSNumber *end = _playAnimation.toValue; - [self _removeCurrentAnimationIfNecessary]; - - _compContainer.currentFrame = frame; - _animationProgress = frame.floatValue / (_sceneModel.endFrame.floatValue - _sceneModel.startFrame.floatValue); - [self playFromFrame:start toFrame:end withCompletion:self.completionBlock]; + NSNumber *frame = [_compContainer.presentationLayer.currentFrame copy]; + [self setProgressWithFrame:frame callCompletionIfNecessary:NO]; + [self playFromFrame:_playRangeStartFrame toFrame:_playRangeEndFrame withCompletion:self.completionBlock]; } } -- (void)setProgressWithFrame:(nonnull NSNumber *)currentFrame { - if (!_sceneModel) { - return; +-(void)setAnimationSpeed:(CGFloat)animationSpeed { + _animationSpeed = animationSpeed; + if (_isAnimationPlaying && _sceneModel) { + NSNumber *frame = [_compContainer.presentationLayer.currentFrame copy]; + [self setProgressWithFrame:frame callCompletionIfNecessary:NO]; + [self playFromFrame:_playRangeStartFrame toFrame:_playRangeEndFrame withCompletion:self.completionBlock]; } - [self _removeCurrentAnimationIfNecessary]; - [self _callCompletionIfNecessary:NO]; - _animationProgress = currentFrame.floatValue / (_sceneModel.endFrame.floatValue - _sceneModel.startFrame.floatValue); - [CATransaction begin]; - [CATransaction setDisableActions:YES]; - _compContainer.currentFrame = currentFrame; - [_compContainer setNeedsDisplay]; - [CATransaction commit]; } +# pragma mark - External Methods - Cache + - (void)setCacheEnable:(BOOL)cacheEnable{ _cacheEnable = cacheEnable; if (!self.cacheKey) { @@ -319,6 +408,8 @@ - (void)setCacheKey:(NSString *)cacheKey { } } +# pragma mark - External Methods - Other + #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR - (void)addSubview:(nonnull LOTView *)view @@ -368,63 +459,16 @@ - (void)setValue:(nonnull id)value } } -- (void)_removeCurrentAnimationIfNecessary { - _playAnimation.speed = 0; - _isAnimationPlaying = NO; - _playAnimation.delegate = nil; - [_compContainer removeAllAnimations]; - _playAnimation = nil; -} - - -# pragma mark - Completion Block - -- (void)_callCompletionIfNecessary:(BOOL)complete { - if (self.completionBlock) { - self.completionBlock(complete); - self.completionBlock = nil; - } +- (void)logHierarchyKeypaths { + [_compContainer logHierarchyKeypathsWithParent:nil]; } # pragma mark - Getters and Setters -- (void)setAnimationProgress:(CGFloat)animationProgress { - if (!_sceneModel) { - _animationProgress = animationProgress; - return; - } - [self _removeCurrentAnimationIfNecessary]; - [self _callCompletionIfNecessary:NO]; - CGFloat duration = _sceneModel.endFrame.floatValue - _sceneModel.startFrame.floatValue; - CGFloat frame = (duration * animationProgress) + _sceneModel.startFrame.floatValue; - _animationProgress = animationProgress; - [CATransaction begin]; - [CATransaction setDisableActions:YES]; - _compContainer.currentFrame = @(frame); - [_compContainer setNeedsDisplay]; - [CATransaction commit]; -} - --(void)setAnimationSpeed:(CGFloat)animationSpeed { +- (CGFloat)animationDuration { if (!_sceneModel) { - _animationSpeed = animationSpeed; - return; + return 0; } - _animationSpeed = animationSpeed; - if (_isAnimationPlaying) { - - NSNumber *frame = [(LOTCompositionContainer *)_compContainer.presentationLayer currentFrame]; - NSNumber *start = _playAnimation.fromValue; - NSNumber *end = _playAnimation.toValue; - [self _removeCurrentAnimationIfNecessary]; - - _compContainer.currentFrame = frame; - _animationProgress = frame.floatValue / (_sceneModel.endFrame.floatValue - _sceneModel.startFrame.floatValue); - [self playFromFrame:start toFrame:end withCompletion:self.completionBlock]; - } -} - -- (CGFloat)animationDuration { if (_playAnimation) { return _playAnimation.duration; } @@ -545,26 +589,16 @@ - (void)_layout { [CATransaction commit]; } +# pragma mark - CAANimationDelegate + - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)complete { if (!_isAnimationPlaying || !complete) { [_compContainer displayWithFrame:_compContainer.currentFrame forceUpdate:YES]; } if (!_isAnimationPlaying || !complete || ![anim isKindOfClass:[CABasicAnimation class]]) return; - NSNumber *frame = [(CABasicAnimation *)anim toValue]; - _animationProgress = frame.floatValue / (_sceneModel.endFrame.floatValue - _sceneModel.startFrame.floatValue); - _isAnimationPlaying = NO; - _playAnimation.delegate = nil; - [_compContainer removeAllAnimations]; - _playAnimation = nil; - [CATransaction begin]; - [CATransaction setDisableActions:YES]; - _compContainer.currentFrame = frame; - [CATransaction commit]; + [self _removeCurrentAnimationIfNecessary]; + [self setProgressWithFrame:_playRangeEndFrame callCompletionIfNecessary:NO]; [self _callCompletionIfNecessary:complete]; } -- (void)logHierarchyKeypaths { - [_compContainer logHierarchyKeypathsWithParent:nil]; -} - @end diff --git a/lottie-ios/Classes/RenderSystem/InterpolatorNodes/LOTValueInterpolator.m b/lottie-ios/Classes/RenderSystem/InterpolatorNodes/LOTValueInterpolator.m index bdbcea82eb..3265522c01 100644 --- a/lottie-ios/Classes/RenderSystem/InterpolatorNodes/LOTValueInterpolator.m +++ b/lottie-ios/Classes/RenderSystem/InterpolatorNodes/LOTValueInterpolator.m @@ -31,6 +31,7 @@ - (id)keyframeDataForValue:(id)value { return nil; } +// Change keyframe data - (BOOL)setValue:(id)value atFrame:(NSNumber *)frame { id data = [self keyframeDataForValue:value]; if (data == nil) { diff --git a/lottie-ios/Classes/RenderSystem/ManipulatorNodes/LOTTrimPathNode.m b/lottie-ios/Classes/RenderSystem/ManipulatorNodes/LOTTrimPathNode.m index 32501a186f..add79b15ce 100644 --- a/lottie-ios/Classes/RenderSystem/ManipulatorNodes/LOTTrimPathNode.m +++ b/lottie-ios/Classes/RenderSystem/ManipulatorNodes/LOTTrimPathNode.m @@ -68,9 +68,9 @@ - (BOOL)updateWithFrame:(NSNumber *)frame if (modifier) { modifier(inputNode); } - + } forceLocalUpdate:(localUpdate || forceUpdate)]; - + return inputUpdated; } diff --git a/lottie-ios/Classes/RenderSystem/RenderNodes/LOTRenderGroup.m b/lottie-ios/Classes/RenderSystem/RenderNodes/LOTRenderGroup.m index 330a132988..06b55dcc55 100644 --- a/lottie-ios/Classes/RenderSystem/RenderNodes/LOTRenderGroup.m +++ b/lottie-ios/Classes/RenderSystem/RenderNodes/LOTRenderGroup.m @@ -28,7 +28,7 @@ @implementation LOTRenderGroup { LOTAnimatorNode *_rootNode; LOTBezierPath *_outputPath; LOTBezierPath *_localPath; - + BOOL _rootNodeHasUpdate; LOTNumberInterpolator *_opacityInterpolator; LOTTransformInterpolator *_transformInterolator; } @@ -126,11 +126,12 @@ - (void)buildContents:(NSArray *)contents { - (BOOL)needsUpdateForFrame:(NSNumber *)frame { return ([_opacityInterpolator hasUpdateForFrame:frame] || [_transformInterolator hasUpdateForFrame:frame] || - [_rootNode needsUpdateForFrame:frame]); + _rootNodeHasUpdate); } - (BOOL)updateWithFrame:(NSNumber *)frame withModifierBlock:(void (^ _Nullable)(LOTAnimatorNode * _Nonnull))modifier forceLocalUpdate:(BOOL)forceUpdate { + _rootNodeHasUpdate = [_rootNode needsUpdateForFrame:frame]; indentation_level = indentation_level + 1; [_rootNode updateWithFrame:frame withModifierBlock:modifier forceLocalUpdate:forceUpdate]; indentation_level = indentation_level - 1;