diff --git a/sparrow/src/Classes/SPGroup.h b/sparrow/src/Classes/SPGroup.h new file mode 100644 index 00000000..1b6c533e --- /dev/null +++ b/sparrow/src/Classes/SPGroup.h @@ -0,0 +1,70 @@ +// +// SPGroup.h +// Sparrow +// +// Created by Jérôme Cabanis on 17/03/2015. +// Copyright 2015 Lauraly. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import +#import + +/** ------------------------------------------------------------------------------------------------ + + The SPGroup takes objects that implement SPAnimatable (e.g. `SPTween`s) and executes them, just like + SPJuggler does but objects can be animated sequentially or in parallel. + + A SPGroup can be added to a SPJuggler or to an other SPGroup + + Furthermore, an object can request to be removed from the SPGroup by dispatching an + `SPEventTypeRemoveFromJuggler` event and SPGroup dispach `SPEventTypeRemoveFromJuggler` event. + + SPGroup provide block-based callbacks that are executed in certain phases of it's life time: + + - `onStart`: Invoked once when the group starts. + - `onUpdate`: Invoked every time it is advanced. + - `onComplete`: Invoked when all objects are completed. + + ------------------------------------------------------------------------------------------------- */ + + +@interface SPGroup : SPEventDispatcher + ++ (instancetype)parallelGroupWithObjects:(NSArray*)objects; ++ (instancetype)serialGroupWithObjects:(NSArray*)objects; + +/// Removes an object from the group. Use this function if the object does not dispatch `SPEventTypeRemoveFromJuggler` events +- (void)removeObject:(id)object; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The delay before the group is started. +@property (nonatomic, assign) double delay; + +/// The total life time of the group. +@property (nonatomic, readonly) double elapsedTime; + +/// The speed factor adjusts how fast the group's animatables run. +/// For example, a speed factor of 2.0 means the group runs twice as fast. +@property (nonatomic, assign) float speed; + +/// Indicates if the group execution is complete. +@property (nonatomic, readonly) BOOL isComplete; + +/// A block that will be called when the group starts (after a possible delay). +@property (nonatomic, copy) SPCallbackBlock onStart; + +/// A block that will be called each time the group is advanced. +@property (nonatomic, copy) SPCallbackBlock onUpdate; + +/// A block that will be called when the group execution is complete. +@property (nonatomic, copy) SPCallbackBlock onComplete; + + +@end diff --git a/sparrow/src/Classes/SPGroup.m b/sparrow/src/Classes/SPGroup.m new file mode 100644 index 00000000..28da1fd5 --- /dev/null +++ b/sparrow/src/Classes/SPGroup.m @@ -0,0 +1,133 @@ +// +// SPGroup.m +// Sparrow +// +// Created by Jérôme on 17/03/2015. +// +// + +#import + +@implementation SPGroup +{ + NSMutableOrderedSet *_objects; + BOOL _serial; + BOOL _started; + BOOL _finished; +} + +#pragma mark Initialization + +- (instancetype)initWithObjects:(NSArray*)objects serial:(BOOL)serial +{ + if ((self = [super init])) + { + _objects = [[NSMutableOrderedSet alloc] init]; + _serial = serial; + _elapsedTime = 0.0; + _speed = 1.0f; + _delay = 0; + _started = NO; + _finished = NO; + + for (id object in objects) + { + if([object conformsToProtocol:@protocol(SPAnimatable)] && ![_objects containsObject:object]) + { + [_objects addObject:object]; + if ([(id)object isKindOfClass:[SPEventDispatcher class]]) + [(SPEventDispatcher *)object addEventListener:@selector(onRemove:) atObject:self forType:SPEventTypeRemoveFromJuggler]; + } + } + } + return self; +} + +- (void)dealloc +{ + [_objects release]; + [super dealloc]; +} + ++ (instancetype)parallelGroupWithObjects:(NSArray*)objects +{ + return [[[SPGroup alloc] initWithObjects:objects serial:NO] autorelease]; +} + ++(instancetype)serialGroupWithObjects:(NSArray *)objects +{ + return [[[SPGroup alloc] initWithObjects:objects serial:YES] autorelease]; +} + +#pragma mark Methods + +- (void)onRemove:(SPEvent *)event +{ + [self removeObject:(id)[[event.target retain] autorelease]]; +} + +- (void)removeObject:(id)object +{ + if([_objects containsObject:object]) + { + [_objects removeObject:object]; + + if ([(id)object isKindOfClass:[SPEventDispatcher class]]) + [(SPEventDispatcher *)object removeEventListenersAtObject:self forType:SPEventTypeRemoveFromJuggler]; + } +} + + +#pragma mark SPAnimatable + +- (void)advanceTime:(double)seconds +{ + if(_isComplete) return; + + if (seconds < 0.0) + [NSException raise:SPExceptionInvalidOperation format:@"time must be positive"]; + + seconds *= _speed; + + if (seconds > 0.0) + { + _elapsedTime += seconds; + + if(_elapsedTime < _delay) + return; + + if(!_started) + { + _started = YES; + if (_onStart) _onStart(); + } + + if(_serial) + { + id object = [_objects firstObject]; + if(object) + [object advanceTime:seconds]; + } + else + { + // we need work with a copy, since user-code could modify the collection while enumerating + NSArray* objectsCopy = [[_objects array] copy]; + + for (id object in objectsCopy) + [object advanceTime:seconds]; + + [objectsCopy release]; + } + + if (_onUpdate) _onUpdate(); + + if([_objects count] == 0) + { + _isComplete = YES; + [self dispatchEventWithType:SPEventTypeRemoveFromJuggler]; + if (_onComplete) _onComplete(); + } + } +} + +@end diff --git a/sparrow/src/Classes/SPJuggler.h b/sparrow/src/Classes/SPJuggler.h index 7e260608..8292c2a2 100644 --- a/sparrow/src/Classes/SPJuggler.h +++ b/sparrow/src/Classes/SPJuggler.h @@ -115,6 +115,9 @@ NS_ASSUME_NONNULL_BEGIN /// For example, a speed factor of 2.0 means the juggler runs twice as fast. @property (nonatomic, assign) float speed; +/// Determines if the juggler is empty. +@property (nonatomic, readonly, getter=isEmpty) BOOL empty; + @end NS_ASSUME_NONNULL_END diff --git a/sparrow/src/Classes/SPJuggler.m b/sparrow/src/Classes/SPJuggler.m index 55c2bf76..db449a1f 100644 --- a/sparrow/src/Classes/SPJuggler.m +++ b/sparrow/src/Classes/SPJuggler.m @@ -114,6 +114,11 @@ - (BOOL)containsObject:(id)object return [_objects containsObject:object]; } +-(BOOL)isEmpty +{ + return [_objects count] == 0; +} + - (id)delayInvocationAtTarget:(id)target byTime:(double)time { SPDelayedInvocation *delayedInv = [SPDelayedInvocation invocationWithTarget:target delay:time]; diff --git a/sparrow/src/Classes/SPTween.h b/sparrow/src/Classes/SPTween.h index 165169cb..4e86fba9 100644 --- a/sparrow/src/Classes/SPTween.h +++ b/sparrow/src/Classes/SPTween.h @@ -107,6 +107,15 @@ typedef float (^SPTransitionBlock)(float); /// is not being animated. - (float)endValueOfProperty:(NSString *)property; +/// Show the target when the tween starts +- (void)show; + +/// Hide the target when the tween is complete +- (void)hide; + +/// Remove the target from his parent when the tween is complete +- (void)removeFromParent; + /// ---------------- /// @name Properties /// ---------------- diff --git a/sparrow/src/Classes/SPTween.m b/sparrow/src/Classes/SPTween.m index 119c59bc..08d7082f 100644 --- a/sparrow/src/Classes/SPTween.m +++ b/sparrow/src/Classes/SPTween.m @@ -19,6 +19,14 @@ typedef float (*FnPtrTransition) (id, SEL, float); +@protocol VisibleProtocol +-(void)setVisible:(BOOL)visible; +@end + +@protocol RemoveFromParentProtocol +- (void)removeFromParent; +@end + @implementation SPTween { id _target; @@ -37,6 +45,9 @@ @implementation SPTween BOOL _reverse; BOOL _roundToInt; NSInteger _currentCycle; + BOOL _show; + BOOL _hide; + BOOL _removeFromParent; SPCallbackBlock _onStart; SPCallbackBlock _onUpdate; @@ -59,6 +70,9 @@ - (instancetype)initWithTarget:(id)target time:(double)time transition:(NSString _repeatCount = 1; _currentCycle = -1; _reverse = NO; + _show = NO; + _hide = NO; + _removeFromParent = NO; self.transition = transition; } return self; @@ -127,6 +141,21 @@ - (void)scaleTo:(float)scale [self animateProperty:@"scaleY" targetValue:scale]; } +-(void)show +{ + _show = YES; +} + +-(void)hide +{ + _hide = YES; +} + +-(void)removeFromParent +{ + _removeFromParent = YES; +} + - (void)fadeTo:(float)alpha { [self animateProperty:@"alpha" targetValue:alpha]; @@ -169,6 +198,8 @@ - (void)advanceTime:(double)time if (isStarting) { _currentCycle++; + if(_show && [_target respondsToSelector:@selector(setVisible:)]) + [(id)_target setVisible:YES]; if (_onStart) _onStart(); } @@ -209,6 +240,10 @@ - (void)advanceTime:(double)time } else { + if(_hide && [_target respondsToSelector:@selector(setVisible:)]) + [(id)_target setVisible:NO]; + if(_removeFromParent && [_target respondsToSelector:@selector(removeFromParent)]) + [(id)_target removeFromParent]; [self dispatchEventWithType:SPEventTypeRemoveFromJuggler]; if (_onComplete) _onComplete(); } diff --git a/sparrow/src/Classes/Sparrow.h b/sparrow/src/Classes/Sparrow.h index 9f8aed08..67cff8b4 100644 --- a/sparrow/src/Classes/Sparrow.h +++ b/sparrow/src/Classes/Sparrow.h @@ -44,6 +44,7 @@ #import #import #import +#import #import #import #import diff --git a/sparrow/src/Sparrow.xcodeproj/project.pbxproj b/sparrow/src/Sparrow.xcodeproj/project.pbxproj index b6feb63f..bbbe71cc 100755 --- a/sparrow/src/Sparrow.xcodeproj/project.pbxproj +++ b/sparrow/src/Sparrow.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 02488C2A1AB8A12800912C73 /* SPGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 02488C281AB8A12800912C73 /* SPGroup.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 02488C2B1AB8A12800912C73 /* SPGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 02488C291AB8A12800912C73 /* SPGroup.m */; }; 7704F8CF1B7D5A8500E9217F /* SparrowBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 7704F8CC1B7D597F00E9217F /* SparrowBase.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7704F8D21B7D5BFD00E9217F /* SparrowBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 7704F8D01B7D5BF200E9217F /* SparrowBase.m */; }; 7728E1A91B7A9704007D1BA7 /* SPGLTexture_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 7728E1A71B7A9704007D1BA7 /* SPGLTexture_Internal.h */; }; @@ -401,6 +403,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 02488C281AB8A12800912C73 /* SPGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPGroup.h; sourceTree = ""; }; + 02488C291AB8A12800912C73 /* SPGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPGroup.m; sourceTree = ""; }; 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 28FD14FF0DC6FC520079059D /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; @@ -993,6 +997,8 @@ DED859440FB883EE00D3D7D2 /* SPTransitions.m */, DE7044750FB62080007F5ECC /* SPTween.h */, DE7044760FB62080007F5ECC /* SPTween.m */, + 02488C281AB8A12800912C73 /* SPGroup.h */, + 02488C291AB8A12800912C73 /* SPGroup.m */, ); name = Animation; sourceTree = ""; @@ -1209,6 +1215,7 @@ DE61137A134F8F6C00AB742E /* SPEventDispatcher.h in Headers */, 872F5C3D1880C9E30016071B /* SPFragmentFilter.h in Headers */, DE61138D134F8FA600AB742E /* SPGLTexture.h in Headers */, + 02488C2A1AB8A12800912C73 /* SPGroup.h in Headers */, DE611388134F8F8300AB742E /* SPImage.h in Headers */, DE611392134F8FBD00AB742E /* SPJuggler.h in Headers */, DE611396134F8FBD00AB742E /* SPMacros.h in Headers */, @@ -1563,6 +1570,7 @@ DE019C3A1026361200ECB0AC /* SPDelayedInvocation.m in Sources */, DE019C3B1026363D00ECB0AC /* SPNSExtensions.m in Sources */, 872F5C541880E57A0016071B /* SPColorMatrix.m in Sources */, + 02488C2B1AB8A12800912C73 /* SPGroup.m in Sources */, DE019C3C1026364800ECB0AC /* SPJuggler.m in Sources */, 872F5C501880E2DD0016071B /* SPDisplacementMapFilter.m in Sources */, 872F5C66188236D80016071B /* SPContext.m in Sources */,