Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A category for Sparrow that allows setting the repeat total time and repeat start values of properties.
//
// SPTween+RepeatValues.h
// Sparrow 2.X
//
// Created by Shilo White on 8/3/13.
//
#import "SPTween.h"
@interface SPTween (RepeatValues)
@property (nonatomic, assign) double repeatTotalTime;
- (void)animateProperty:(NSString*)property targetValue:(float)endValue repeatStartValue:(float)repeatStartValue;
@end
//
// SPTween+RepeatValues.m
// Sparrow 2.X
//
// Created by Shilo White on 8/3/13.
//
#import "SPTween+RepeatValues.h"
#import "SPTransitions.h"
#import "SPTweenedProperty.h"
#import <objc/runtime.h>
#define TRANS_SUFFIX @":"
typedef float (*FnPtrTransition) (id, SEL, float);
@interface SPTweenedProperty (RepeatValues)
@property (nonatomic, assign) NSNumber *repeatStartValue;
@end
@interface SPTween () {
id _target;
SEL _transition;
IMP _transitionFunc;
NSMutableArray *_properties;
double _totalTime;
double _currentTime;
double _delay;
int _repeatCount;
double _repeatDelay;
BOOL _reverse;
int _currentCycle;
SPCallbackBlock _onStart;
SPCallbackBlock _onUpdate;
SPCallbackBlock _onRepeat;
SPCallbackBlock _onComplete;
}
@end
@implementation SPTween (RepeatValues)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
- (id)initWithTarget:(id)target time:(double)time transition:(NSString*)transition
{
if ((self = [super init]))
{
_target = target;
self.repeatTotalTime = _totalTime = MAX(0.0001, time); // zero is not allowed
_currentTime = 0;
_delay = 0;
_properties = [[NSMutableArray alloc] init];
_repeatCount = 1;
_currentCycle = -1;
_reverse = NO;
// create function pointer for transition
NSString *transMethod = [transition stringByAppendingString:TRANS_SUFFIX];
_transition = NSSelectorFromString(transMethod);
if (![SPTransitions respondsToSelector:_transition])
[NSException raise:SP_EXC_INVALID_OPERATION
format:@"transition not found: '%@'", transition];
_transitionFunc = [SPTransitions methodForSelector:_transition];
}
return self;
}
- (void)setRepeatTotalTime:(double)repeatTotalTime
{
objc_setAssociatedObject(self, @"repeatTotalTime", [NSNumber numberWithDouble:repeatTotalTime], OBJC_ASSOCIATION_RETAIN);
}
- (double)repeatTotalTime
{
return [objc_getAssociatedObject(self, @"repeatTotalTime") doubleValue];
}
- (void)animateProperty:(NSString*)property targetValue:(float)endValue repeatStartValue:(float)repeatStartValue
{
if (!_target) return;
SPTweenedProperty *tweenedProp = [[SPTweenedProperty alloc] initWithTarget:_target name:property endValue:endValue];
tweenedProp.repeatStartValue = [NSNumber numberWithFloat:repeatStartValue];
[_properties addObject:tweenedProp];
}
- (void)advanceTime:(double)time
{
if (time == 0.0 || (_repeatCount == 1 && _currentTime == _totalTime))
return; // nothing to do
else if ((_repeatCount == 0 || _repeatCount > 1) && _currentTime == _totalTime)
_currentTime = 0.0;
double previousTime = _currentTime;
double restTime = _totalTime - _currentTime;
double carryOverTime = time > restTime ? time - restTime : 0.0;
_currentTime = MIN(_totalTime, _currentTime + time);
BOOL isStarting = _currentCycle < 0 && previousTime <= 0 && _currentTime > 0;
if (_currentTime <= 0) return; // the delay is not over yet
if (isStarting)
{
_currentCycle++;
if (_onStart) _onStart();
}
float ratio = _currentTime / _totalTime;
BOOL reversed = _reverse && (_currentCycle % 2 == 1);
FnPtrTransition transFunc = (FnPtrTransition) _transitionFunc;
Class transClass = [SPTransitions class];
for (SPTweenedProperty *prop in _properties)
{
if (isStarting) prop.startValue = prop.currentValue;
float transitionValue = reversed ? transFunc(transClass, _transition, 1.0 - ratio) :
transFunc(transClass, _transition, ratio);
prop.currentValue = prop.startValue + prop.delta * transitionValue;
}
if (_onUpdate) _onUpdate();
if (previousTime < _totalTime && _currentTime >= _totalTime)
{
if (_repeatCount == 0 || _repeatCount > 1)
{
if (_currentCycle == 0)
{
_totalTime = self.repeatTotalTime;
for (SPTweenedProperty *prop in _properties)
if (prop.repeatStartValue)
prop.startValue = [prop.repeatStartValue floatValue];
}
_currentTime = -_repeatDelay;
_currentCycle++;
if (_repeatCount > 1) _repeatCount--;
if (_onRepeat) _onRepeat();
}
else
{
[self dispatchEventWithType:SP_EVENT_TYPE_REMOVE_FROM_JUGGLER];
if (_onComplete) _onComplete();
}
}
if (carryOverTime)
[self advanceTime:carryOverTime];
}
#pragma clang diagnostic pop
@end
@implementation SPTweenedProperty (RepeatValues)
- (void)setRepeatStartValue:(NSNumber *)repeatStartValue
{
objc_setAssociatedObject(self, @"repeatStartValue", repeatStartValue, OBJC_ASSOCIATION_RETAIN);
}
- (NSNumber *)repeatStartValue
{
return objc_getAssociatedObject(self, @"repeatStartValue");
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.