热更新demo

IFTTTFilmstrip.m 3.8KB

    // // IFTTTFilmstrip.m // JazzHands // // Created by Laura Skelton on 6/17/15. // Copyright (c) 2015 IFTTT Inc. All rights reserved. // #import "IFTTTFilmstrip.h" #import "IFTTTInterpolatable.h" #pragma mark - IFTTTKeyframe @interface IFTTTKeyframe : NSObject @property (nonatomic, assign) CGFloat time; @property (nonatomic, strong) id<IFTTTInterpolatable> value; @property (nonatomic, copy) IFTTTEasingFunction easingFunction; - (instancetype)initWithTime:(CGFloat)time value:(id)value easingFunction:(IFTTTEasingFunction)easingFunction; @end @implementation IFTTTKeyframe - (instancetype)initWithTime:(CGFloat)time value:(id)value easingFunction:(IFTTTEasingFunction)easingFunction { if ((self = [super init])) { _time = time; _value = value; _easingFunction = easingFunction; } return self; } @end #pragma mark - IFTTTFilmstrip @interface IFTTTFilmstrip () @property (nonatomic, strong) NSMutableArray *keyframes; @end @implementation IFTTTFilmstrip - (instancetype)init { if ((self = [super init])) { _keyframes = [NSMutableArray array]; } return self; } - (BOOL)isEmpty { return (self.keyframes.count == 0); } - (void)setValue:(id<IFTTTInterpolatable>)value atTime:(CGFloat)time { [self setValue:value atTime:time withEasingFunction:IFTTTEasingFunctionLinear]; } - (void)setValue:(id<IFTTTInterpolatable>)value atTime:(CGFloat)time withEasingFunction:(IFTTTEasingFunction)easingFunction { NSAssert([self canInterpolateNewValue:value], @"New value must have the same interpolatable type as existing keyframe values."); IFTTTKeyframe *newKeyframe = [[IFTTTKeyframe alloc] initWithTime:time value:value easingFunction:easingFunction]; NSUInteger indexAfter = [self indexOfKeyframeAfterTime:time]; [self.keyframes insertObject:newKeyframe atIndex:indexAfter]; } - (id<IFTTTInterpolatable>)valueAtTime:(CGFloat)time { NSAssert(!self.isEmpty, @"At least one KeyFrame must be set before animation begins."); id value; NSUInteger indexAfter = [self indexOfKeyframeAfterTime:time]; if (indexAfter == 0) { value = ((IFTTTKeyframe *)self.keyframes[0]).value; } else if (indexAfter < self.keyframes.count) { IFTTTKeyframe *keyframeBefore = (IFTTTKeyframe *)self.keyframes[indexAfter - 1]; IFTTTKeyframe *keyframeAfter = (IFTTTKeyframe *)self.keyframes[indexAfter]; CGFloat progress = [self progressFromTime:keyframeBefore.time toTime:keyframeAfter.time atTime:time withEasingFunction:keyframeBefore.easingFunction]; value = [keyframeBefore.value interpolateTo:keyframeAfter.value withProgress:progress]; } else { value = ((IFTTTKeyframe *)self.keyframes.lastObject).value; } return value; } - (NSUInteger)indexOfKeyframeAfterTime:(CGFloat)time { for (NSUInteger i = 0; i < self.keyframes.count; i++) { IFTTTKeyframe *keyframe = (IFTTTKeyframe *)[self.keyframes objectAtIndex:i]; if (time < keyframe.time) { return i; } } return self.keyframes.count; } - (CGFloat)progressFromTime:(CGFloat)fromTime toTime:(CGFloat)toTime atTime:(CGFloat)atTime withEasingFunction:(IFTTTEasingFunction)easingFunction { CGFloat duration = toTime - fromTime; if (duration == 0.f) return 0.f; CGFloat timeElapsed = atTime - fromTime; return easingFunction(timeElapsed / duration); } - (BOOL)canInterpolateNewValue:(id)newValue { if (self.keyframes.count == 0) { return YES; } IFTTTKeyframe *existingKeyframe = (IFTTTKeyframe *)self.keyframes.firstObject; return ([newValue respondsToSelector:@selector(interpolateTo:withProgress:)] && ([newValue isKindOfClass:[existingKeyframe.value class]] || ([existingKeyframe.value isKindOfClass:[UIColor class]] && [newValue isKindOfClass:[UIColor class]]))); } @end