├── .gitignore ├── .gitmodules ├── .travis.yml ├── CFAAction.podspec ├── CFAAction.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ ├── CFAAction-iOS.xcscheme │ └── CFAAction.xcscheme ├── CFAAction ├── CFAAction+Private.h ├── CFAAction-Info.plist ├── CFAAction-Prefix.pch ├── CFAAction.h ├── CFAAction.m ├── CFACustomAction.h ├── CFACustomAction.m ├── CFAFade.h ├── CFAFade.m ├── CFAFollowPath.h ├── CFAFollowPath.m ├── CFAGroup.h ├── CFAGroup.m ├── CFAHide.h ├── CFAHide.m ├── CFAMove.h ├── CFAMove.m ├── CFAPerformSelector.h ├── CFAPerformSelector.m ├── CFARepeat.h ├── CFARepeat.m ├── CFAResize.h ├── CFAResize.m ├── CFARotate.h ├── CFARotate.m ├── CFARunBlock.h ├── CFARunBlock.m ├── CFAScale.h ├── CFAScale.m ├── CFASequence.h ├── CFASequence.m ├── CFASpeed.h ├── CFASpeed.m ├── CFAWait.h ├── CFAWait.m ├── NSBezierPath+CFABezierPathQuartzAssistant.h ├── NSBezierPath+CFABezierPathQuartzAssistant.m └── en.lproj │ └── InfoPlist.strings ├── CFAActionTests ├── CFAActionSpec.m ├── CFAActionTests-Info.plist ├── CFAActionTests-Prefix.pch ├── CFAPerformSelectorSpec.m ├── CFARunBlockSpec.m ├── CFATestLayer.h ├── CFATestLayer.m └── en.lproj │ └── InfoPlist.strings ├── Example └── CAActionDemo │ ├── CAActionDemo.xcodeproj │ └── project.pbxproj │ └── CAActionDemo │ ├── Base.lproj │ └── MainMenu.xib │ ├── CAActionDemo-Info.plist │ ├── CAActionDemo-Prefix.pch │ ├── CFIAppDelegate.h │ ├── CFIAppDelegate.m │ ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── en.lproj │ ├── Credits.rtf │ └── InfoPlist.strings │ └── main.m ├── LICENSE.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | */build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | *.hmap 20 | *.xcuserstate 21 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "External/Expecta"] 2 | path = External/Expecta 3 | url = https://github.com/specta/expecta.git 4 | [submodule "External/Specta"] 5 | path = External/Specta 6 | url = https://github.com/specta/specta.git 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode7.3 3 | 4 | script: 5 | - set -o pipefail 6 | - xcodebuild test -scheme CFAAction | xcpretty -c 7 | - xcodebuild build -scheme CFAAction-iOS -destination 'platform=iOS Simulator,name=iPad Pro' | xcpretty -c 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /CFAAction.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "CFAAction" 3 | s.version = "0.1.0" 4 | s.summary = "Composable Core Animation Actions à la SceneKit" 5 | s.homepage = "https://github.com/CodaFi/CFAAction" 6 | s.license = { :type => "MIT", :text => <<-LICENSE 7 | The MIT License (MIT) 8 | 9 | Copyright (c) 2015-2016 Robert Widmann 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | 29 | LICENSE 30 | } 31 | s.authors = { "CodaFi" => "devteam.codafi@gmail.com" } 32 | 33 | s.requires_arc = true 34 | s.osx.deployment_target = "10.8" 35 | s.ios.deployment_target = "7.0" 36 | s.source = { :git => "https://github.com/CodaFi/CFAAction.git", :tag => "v#{s.version}", :submodules => true } 37 | s.source_files = "CFAAction/*.{h,m}" 38 | end 39 | 40 | -------------------------------------------------------------------------------- /CFAAction.xcodeproj/xcshareddata/xcschemes/CFAAction-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 74 | 75 | 77 | 78 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /CFAAction.xcodeproj/xcshareddata/xcschemes/CFAAction.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 55 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /CFAAction/CFAAction+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFAAction_Private.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/16/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAAction.h" 11 | 12 | @interface CFAAction () 13 | 14 | @property (nonatomic) BOOL finished; 15 | @property (NS_NONATOMIC_IOSONLY) float repeatCount; 16 | @property (nonatomic) CAMediaTimingFunction *timingFunction; 17 | 18 | - (void)executeWithTarget:(CALayer *)target forTime:(NSTimeInterval)time; 19 | - (void)setOnCompletion:(void(^)(void))block; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /CFAAction/CFAAction-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSHumanReadableCopyright 26 | Copyright © 2013 CodaFi. All rights reserved. 27 | NSPrincipalClass 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /CFAAction/CFAAction-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #define OBJC_OLD_DISPATCH_PROTOTYPES 0 8 | 9 | #ifdef __OBJC__ 10 | #import 11 | #endif 12 | -------------------------------------------------------------------------------- /CFAAction/CFAAction.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFAAction.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/16/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import 11 | 12 | /// The modes that an action can use to adjust the apparent timing of the action. 13 | typedef NS_ENUM(NSInteger, CFAActionTimingMode) { 14 | /// Specifies linear pacing. Linear pacing causes an animation to occur evenly over its duration. 15 | CFAActionTimingLinear, 16 | /// Specifies ease-in pacing. Ease-in pacing causes the animation to begin slowly, and then 17 | /// speed up as it progresses. 18 | CFAActionTimingEaseIn, 19 | /// Specifies ease-out pacing. Ease-out pacing causes the animation to begin quickly, and then 20 | /// slow as it completes. 21 | CFAActionTimingEaseOut, 22 | /// Specifies ease-in ease-out pacing. An ease-in ease-out animation begins slowly, accelerates 23 | /// through the middle of its duration, and then slows again before completing. 24 | CFAActionTimingEaseInEaseOut 25 | }; 26 | 27 | NS_ASSUME_NONNULL_BEGIN 28 | 29 | /// A CFAAction is an action that is executed on a layer. Actions are most often used to change the 30 | /// structure and appearance of layers, but can also be used to make other changes to the layer tree, 31 | /// or execute custom actions. 32 | /// 33 | /// Most actions allow you to change a layer's properties such as its position, rotation, or scale. 34 | /// When an action is animated, the duration property states how long the action takes to complete in 35 | /// seconds, and its timing mode property determines the rate at which the animation executes. 36 | /// 37 | /// Many actions can be reversed, allowing you to create another action object that reverses the 38 | /// effect of that action. Actions that cannot be reversed will often return the same action, or a 39 | /// copy of the original. 40 | /// 41 | /// Some actions include other actions as children: 42 | /// 43 | /// - A sequence action has multiple child actions. Each action in the sequence begins after the 44 | /// previous action ends. 45 | /// - A group action has multiple child actions. All actions stored in the group begin executing at 46 | /// the same time. 47 | /// - A repeating action stores a single child action. When the child action completes, it is 48 | /// restarted. 49 | /// - Groups, sequences, and repeating actions can be nested. The ability to combine actions together 50 | /// allows you to add very sophisticated behaviors to a node. 51 | /// 52 | @interface CFAAction : NSObject 53 | 54 | /// The duration required to complete an action. 55 | @property (NS_NONATOMIC_IOSONLY) NSTimeInterval duration; 56 | 57 | /// The timing mode used to execute an action. 58 | @property (NS_NONATOMIC_IOSONLY) CFAActionTimingMode timingMode; 59 | 60 | /// A speed factor that modifies how fast an action runs. 61 | @property (NS_NONATOMIC_IOSONLY) CGFloat speed; 62 | 63 | /// Creates an action that reverses the behavior of another action. 64 | - (CFAAction *)reversedAction; 65 | 66 | @end 67 | 68 | @interface CFAAction (CFAActions) 69 | 70 | 71 | #pragma mark - Move Actions 72 | 73 | 74 | /// Creates an action that moves a node relative to its current position. 75 | + (CFAAction *)moveByX:(CGFloat)deltaX y:(CGFloat)deltaY duration:(NSTimeInterval)sec; 76 | 77 | #if CGVECTOR_DEFINED 78 | /// Creates an action that moves a node relative to its current position. 79 | + (CFAAction *)moveBy:(CGVector)delta duration:(NSTimeInterval)sec; 80 | #endif 81 | 82 | /// Creates an action that moves a node to a new position. 83 | + (CFAAction *)moveTo:(CGPoint)location duration:(NSTimeInterval)sec; 84 | 85 | /// Creates an action that moves a node horizontally. 86 | + (CFAAction *)moveToX:(CGFloat)x duration:(NSTimeInterval)sec; 87 | 88 | /// Creates an action that moves a node vertically. 89 | + (CFAAction *)moveToY:(CGFloat)y duration:(NSTimeInterval)sec; 90 | 91 | /// Creates an action that moves the node along a relative path, orienting the node to the path. 92 | + (CFAAction *)followPath:(CGPathRef)path duration:(NSTimeInterval)sec; 93 | 94 | /// Creates an action that moves the node along a path. 95 | + (CFAAction *)followPath:(CGPathRef)path asOffset:(BOOL)offset orientToPath:(BOOL)orient duration:(NSTimeInterval)sec; 96 | 97 | 98 | #pragma mark - Rotate Actions 99 | 100 | 101 | /// Creates an action that rotates the node by a relative value. 102 | + (CFAAction *)rotateByAngle:(CGFloat)radians duration:(NSTimeInterval)sec; 103 | 104 | /// Creates an action that rotates the node counterclockwise to an absolute angle. 105 | + (CFAAction *)rotateToAngle:(CGFloat)radians duration:(NSTimeInterval)sec; 106 | 107 | /// Creates an action that rotates the node to an absolute value. 108 | + (CFAAction *)rotateToAngle:(CGFloat)radians duration:(NSTimeInterval)sec shortestUnitArc:(BOOL)shortestUnitArc; 109 | 110 | 111 | #pragma mark - Transparency Actions 112 | 113 | 114 | /// Creates an action that changes the alpha value of the node to 1.0. 115 | + (CFAAction *)fadeInWithDuration:(NSTimeInterval)sec; 116 | 117 | /// Creates an action that changes the alpha value of the node to 0.0. 118 | + (CFAAction *)fadeOutWithDuration:(NSTimeInterval)sec; 119 | 120 | /// Creates an action that adjusts the alpha value of a node by a relative value. 121 | + (CFAAction *)fadeAlphaBy:(CGFloat)factor duration:(NSTimeInterval)sec; 122 | 123 | /// Creates an action that adjusts the alpha value of a node to a new value. 124 | + (CFAAction *)fadeAlphaTo:(CGFloat)alpha duration:(NSTimeInterval)sec; 125 | 126 | 127 | #pragma mark - Resize Actions 128 | 129 | 130 | /// Creates an action that adjusts the size of a sprite. 131 | + (CFAAction *)resizeByWidth:(CGFloat)width height:(CGFloat)height duration:(NSTimeInterval)duration; 132 | 133 | /// Creates an action that changes the width and height of a sprite to a new absolute value. 134 | + (CFAAction *)resizeToWidth:(CGFloat)width height:(CGFloat)height duration:(NSTimeInterval)duration; 135 | 136 | /// Creates an action that changes the width of a sprite to a new absolute value. 137 | + (CFAAction *)resizeToWidth:(CGFloat)width duration:(NSTimeInterval)duration; 138 | 139 | /// Creates an action that changes the height of a sprite to a new absolute value. 140 | + (CFAAction *)resizeToHeight:(CGFloat)height duration:(NSTimeInterval)duration; 141 | 142 | 143 | #pragma mark - Scale Actions 144 | 145 | 146 | /// Creates an action that changes the x and y scale values of a node by a relative value. 147 | + (CFAAction *)scaleBy:(CGFloat)scale duration:(NSTimeInterval)sec; 148 | 149 | /// Creates an action that adds relative values to the x and y scale values of a node. 150 | + (CFAAction *)scaleXBy:(CGFloat)xScale y:(CGFloat)yScale duration:(NSTimeInterval)sec; 151 | 152 | /// Creates an action that changes the x and y scale values of a node. 153 | + (CFAAction *)scaleTo:(CGFloat)scale duration:(NSTimeInterval)sec; 154 | 155 | /// Creates an action that changes the x and y scale values of a node. 156 | + (CFAAction *)scaleXTo:(CGFloat)xScale y:(CGFloat)yScale duration:(NSTimeInterval)sec; 157 | 158 | /// Creates an action that changes the x scale value of a node to a new value. 159 | + (CFAAction *)scaleXTo:(CGFloat)scale duration:(NSTimeInterval)sec; 160 | 161 | /// Creates an action that changes the y scale value of a node to a new value. 162 | + (CFAAction *)scaleYTo:(CGFloat)scale duration:(NSTimeInterval)sec; 163 | 164 | #pragma mark - Show or Hide Actions 165 | 166 | /// Creates an action that hides a node. 167 | + (CFAAction *)hide; 168 | 169 | /// Creates an action that unhides a node. 170 | + (CFAAction *)unhide; 171 | 172 | 173 | #pragma mark - Combine Actions 174 | 175 | 176 | /// Creates an action that runs a collection of actions sequentially. 177 | + (CFAAction *)sequence:(NSArray *)actions; 178 | 179 | /// Creates an action that runs a collection of actions in parallel. 180 | + (CFAAction *)group:(NSArray *)actions; 181 | 182 | 183 | #pragma mark - Repeat Actions 184 | 185 | 186 | /// Creates an action that repeats another action a specified number of times. 187 | + (CFAAction *)repeatAction:(CFAAction *)action count:(NSUInteger)count; 188 | 189 | /// Creates an action that repeats another action forever. 190 | + (CFAAction *)repeatActionForever:(CFAAction *)action; 191 | 192 | 193 | #pragma mark - Speed Actions 194 | 195 | 196 | /// Creates an action that changes how fast the node executes actions by a relative value. 197 | + (CFAAction *)speedBy:(CGFloat)speed duration:(NSTimeInterval)sec; 198 | 199 | /// Creates an action that changes how fast the node executes actions. 200 | + (CFAAction *)speedTo:(CGFloat)speed duration:(NSTimeInterval)sec; 201 | 202 | 203 | #pragma mark - Delay Actions 204 | 205 | 206 | /// Creates an action that idles for a specified period of time. 207 | + (CFAAction *)waitForDuration:(NSTimeInterval)sec; 208 | 209 | /// Creates an action that idles for a randomized period of time. 210 | + (CFAAction *)waitForDuration:(NSTimeInterval)sec withRange:(NSTimeInterval)durationRange; 211 | 212 | 213 | #pragma mark - Custom Actions 214 | 215 | 216 | /// Creates an action that calls a method on an object. 217 | + (CFAAction *)performSelector:(SEL)selector onTarget:(id)target; 218 | 219 | /// Creates an action that executes a block. 220 | + (CFAAction *)runBlock:(dispatch_block_t)block; 221 | 222 | /// Creates an action that executes a block on a specific dispatch queue. 223 | + (CFAAction *)runBlock:(dispatch_block_t)block queue:(dispatch_queue_t)queue; 224 | 225 | /// Creates an action that executes a block over a duration. 226 | + (CFAAction *)customActionWithDuration:(NSTimeInterval)seconds actionBlock:(void (^)(CALayer *node, CGFloat elapsedTime))block; 227 | 228 | @end 229 | 230 | @interface CALayer (CFAAction) 231 | 232 | /// Adds an action to the list of actions executed by the node. 233 | - (void)cfa_runAction:(CFAAction *)action; 234 | 235 | /// Adds an action to the list of actions executed by the node. Your block is called when the action 236 | /// completes. 237 | - (void)cfa_runAction:(CFAAction *)action completion:(void (^)(void))block; 238 | 239 | @end 240 | 241 | NS_ASSUME_NONNULL_END 242 | -------------------------------------------------------------------------------- /CFAAction/CFAAction.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFAAction.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/16/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAAction+Private.h" 11 | #import "CFARunBlock.h" 12 | #import "CFASequence.h" 13 | #import "CFAGroup.h" 14 | #import "CFAHide.h" 15 | #import "CFACustomAction.h" 16 | #import "CFAFade.h" 17 | #import "CFAFollowPath.h" 18 | #import "CFAMove.h" 19 | #import "CFAPerformSelector.h" 20 | #import "CFARotate.h" 21 | #import "CFAResize.h" 22 | #import "CFARepeat.h" 23 | #import "CFAScale.h" 24 | #import "CFASpeed.h" 25 | #import "CFAWait.h" 26 | 27 | @implementation CFAAction { 28 | BOOL _finished; 29 | void (^_completionBlock)(void); 30 | } 31 | 32 | - (id)copyWithZone:(NSZone *)zone { 33 | CFAAction *copyAction = [[self.class alloc] init]; 34 | copyAction.duration = self.duration; 35 | copyAction.timingMode = self.timingMode; 36 | copyAction.speed = self.speed; 37 | 38 | return copyAction; 39 | } 40 | 41 | - (CFAAction *)reversedAction { 42 | return [self copy]; 43 | } 44 | 45 | // Override point for subclasses 46 | - (void)executeWithTarget:(CALayer *)target forTime:(NSTimeInterval)time { } 47 | 48 | - (void)setOnCompletion:(void(^)(void))block { 49 | _completionBlock = [block copy]; 50 | } 51 | 52 | - (void)setFinished:(BOOL)finished { 53 | if (finished && _completionBlock) { 54 | _completionBlock(); 55 | } 56 | _finished = finished; 57 | } 58 | 59 | - (CAMediaTimingFunction *)timingFunction { 60 | return [CAMediaTimingFunction functionWithName:CFAMediaTimingFunctionName[self.timingMode]]; 61 | } 62 | 63 | static NSString * CFAMediaTimingFunctionName[4] = { 64 | @"linear", // CFAActionTimingLinear 65 | @"easeIn", // CFAActionTimingEaseIn 66 | @"easeOut", // CFAActionTimingEaseOut 67 | @"easeInOut" // CFAActionTimingEaseInEaseOut 68 | }; 69 | 70 | @end 71 | 72 | @implementation CFAAction (CFAActions) 73 | 74 | + (CFAAction *)moveByX:(CGFloat)deltaX y:(CGFloat)deltaY duration:(NSTimeInterval)sec { 75 | return [CFAMove moveByX:deltaX y:deltaY duration:sec]; 76 | } 77 | 78 | #if CGVECTOR_DEFINED 79 | + (CFAAction *)moveBy:(CGVector)delta duration:(NSTimeInterval)sec { 80 | return [CFAMove moveBy:delta duration:sec]; 81 | } 82 | #endif 83 | 84 | + (CFAAction *)moveTo:(CGPoint)location duration:(NSTimeInterval)sec { 85 | return [CFAMove moveTo:location duration:sec]; 86 | } 87 | 88 | + (CFAAction *)moveToX:(CGFloat)x duration:(NSTimeInterval)sec { 89 | return [CFAMove moveToX:x duration:sec]; 90 | } 91 | 92 | + (CFAAction *)moveToY:(CGFloat)y duration:(NSTimeInterval)sec { 93 | return [CFAMove moveToY:y duration:sec]; 94 | } 95 | 96 | + (CFAAction *)rotateByAngle:(CGFloat)radians duration:(NSTimeInterval)sec { 97 | return [CFARotate rotateByAngle:radians duration:sec]; 98 | } 99 | 100 | + (CFAAction *)rotateToAngle:(CGFloat)radians duration:(NSTimeInterval)sec { 101 | return [CFARotate rotateToAngle:radians duration:sec]; 102 | } 103 | 104 | + (CFAAction *)rotateToAngle:(CGFloat)radians duration:(NSTimeInterval)sec shortestUnitArc:(BOOL)shortestUnitArc { 105 | return [CFARotate rotateToAngle:radians duration:sec shortestUnitArc:shortestUnitArc]; 106 | } 107 | 108 | + (CFAAction *)resizeByWidth:(CGFloat)width height:(CGFloat)height duration:(NSTimeInterval)duration { 109 | return [CFAResize resizeByWidth:width height:height duration:duration]; 110 | } 111 | 112 | + (CFAAction *)resizeToWidth:(CGFloat)width height:(CGFloat)height duration:(NSTimeInterval)duration { 113 | return [CFAResize resizeToWidth:width height:height duration:duration]; 114 | } 115 | 116 | + (CFAAction *)resizeToWidth:(CGFloat)width duration:(NSTimeInterval)duration { 117 | return [CFAResize resizeToWidth:width duration:duration]; 118 | } 119 | 120 | + (CFAAction *)resizeToHeight:(CGFloat)height duration:(NSTimeInterval)duration { 121 | return [CFAResize resizeToHeight:height duration:duration]; 122 | } 123 | 124 | + (CFAAction *)scaleBy:(CGFloat)scale duration:(NSTimeInterval)sec { 125 | return [CFAScale scaleBy:scale duration:sec]; 126 | } 127 | 128 | + (CFAAction *)scaleXBy:(CGFloat)xScale y:(CGFloat)yScale duration:(NSTimeInterval)sec { 129 | return [CFAScale scaleXBy:xScale y:yScale duration:sec]; 130 | } 131 | 132 | + (CFAAction *)scaleTo:(CGFloat)scale duration:(NSTimeInterval)sec { 133 | return [CFAScale scaleTo:scale duration:sec]; 134 | } 135 | 136 | + (CFAAction *)scaleXTo:(CGFloat)xScale y:(CGFloat)yScale duration:(NSTimeInterval)sec { 137 | return [CFAScale scaleXTo:xScale y:yScale duration:sec]; 138 | } 139 | 140 | + (CFAAction *)scaleXTo:(CGFloat)scale duration:(NSTimeInterval)sec { 141 | return [CFAScale scaleXTo:scale duration:sec]; 142 | } 143 | 144 | + (CFAAction *)scaleYTo:(CGFloat)scale duration:(NSTimeInterval)sec { 145 | return [CFAScale scaleYTo:scale duration:sec]; 146 | } 147 | 148 | + (CFAAction *)hide { 149 | return [CFAHide hide]; 150 | } 151 | 152 | + (CFAAction *)unhide { 153 | return [CFAHide unhide]; 154 | } 155 | 156 | + (CFAAction *)sequence:(NSArray *)actions { 157 | return [CFASequence sequenceWithActions:actions]; 158 | } 159 | 160 | + (CFAAction *)group:(NSArray *)actions { 161 | return [CFAGroup groupWithActions:actions]; 162 | } 163 | 164 | + (CFAAction *)repeatAction:(CFAAction *)action count:(NSUInteger)count { 165 | return [CFARepeat repeatAction:action count:count]; 166 | } 167 | 168 | + (CFAAction *)repeatActionForever:(CFAAction *)action { 169 | return [CFARepeat repeatActionForever:action]; 170 | } 171 | 172 | + (CFAAction *)fadeInWithDuration:(NSTimeInterval)sec { 173 | return [CFAFade fadeInWithDuration:sec]; 174 | } 175 | 176 | + (CFAAction *)fadeOutWithDuration:(NSTimeInterval)sec { 177 | return [CFAFade fadeOutWithDuration:sec]; 178 | } 179 | 180 | + (CFAAction *)fadeAlphaBy:(CGFloat)factor duration:(NSTimeInterval)sec { 181 | return [CFAFade fadeAlphaBy:factor duration:sec]; 182 | } 183 | 184 | + (CFAAction *)fadeAlphaTo:(CGFloat)alpha duration:(NSTimeInterval)sec { 185 | return [CFAFade fadeAlphaTo:alpha duration:sec]; 186 | } 187 | 188 | + (CFAAction *)followPath:(CGPathRef)path duration:(NSTimeInterval)sec { 189 | return [CFAFollowPath followPath:path duration:sec]; 190 | } 191 | 192 | + (CFAAction *)followPath:(CGPathRef)path asOffset:(BOOL)offset orientToPath:(BOOL)orient duration:(NSTimeInterval)sec { 193 | return [CFAFollowPath followPath:path asOffset:offset orientToPath:orient duration:sec]; 194 | } 195 | 196 | + (CFAAction *)speedBy:(CGFloat)speed duration:(NSTimeInterval)sec { 197 | return [CFASpeed speedBy:speed duration:sec]; 198 | } 199 | 200 | + (CFAAction *)speedTo:(CGFloat)speed duration:(NSTimeInterval)sec { 201 | return [CFASpeed speedTo:speed duration:sec]; 202 | } 203 | 204 | + (CFAAction *)waitForDuration:(NSTimeInterval)sec { 205 | return [CFAWait waitForDuration:sec]; 206 | } 207 | 208 | + (CFAAction *)waitForDuration:(NSTimeInterval)sec withRange:(NSTimeInterval)durationRange { 209 | return [CFAWait waitForDuration:sec withRange:durationRange]; 210 | } 211 | 212 | + (CFAAction *)performSelector:(SEL)selector onTarget:(id)target { 213 | return [CFAPerformSelector performSelector:selector onTarget:target]; 214 | } 215 | 216 | + (CFAAction *)runBlock:(dispatch_block_t)block { 217 | return [CFARunBlock runBlock:block queue:dispatch_get_main_queue()]; 218 | } 219 | 220 | + (CFAAction *)runBlock:(dispatch_block_t)block queue:(dispatch_queue_t)queue { 221 | return [CFARunBlock runBlock:block queue:queue]; 222 | } 223 | 224 | + (CFAAction *)customActionWithDuration:(NSTimeInterval)seconds actionBlock:(void (^)(CALayer *node, CGFloat elapsedTime))block { 225 | return [CFACustomAction customActionWithDuration:seconds actionBlock:block]; 226 | } 227 | 228 | @end 229 | 230 | @implementation CALayer (CFAAction) 231 | 232 | - (void)cfa_runAction:(CFAAction *)action { 233 | [action executeWithTarget:self forTime:CACurrentMediaTime()]; 234 | } 235 | 236 | - (void)cfa_runAction:(CFAAction *)action completion:(void (^)(void))block { 237 | [action setOnCompletion:block]; 238 | [action executeWithTarget:self forTime:CACurrentMediaTime()]; 239 | } 240 | 241 | @end -------------------------------------------------------------------------------- /CFAAction/CFACustomAction.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFACustomAction.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAAction+Private.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface CFACustomAction : CFAAction 15 | 16 | + (CFAAction *)customActionWithDuration:(NSTimeInterval)seconds actionBlock:(void (^)(CALayer *node, CGFloat elapsedTime))block; 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /CFAAction/CFACustomAction.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFACustomAction.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFACustomAction.h" 11 | 12 | @implementation CFACustomAction { 13 | void (^_actionBlock)(CALayer *node, CGFloat elapsedTime); 14 | } 15 | 16 | + (CFAAction *)customActionWithDuration:(NSTimeInterval)seconds actionBlock:(void (^)(CALayer *node, CGFloat elapsedTime))block { 17 | CFACustomAction *action = [[CFACustomAction alloc] init]; 18 | 19 | action->_actionBlock = [block copy]; 20 | action.duration = seconds; 21 | 22 | return action; 23 | } 24 | 25 | - (id)copyWithZone:(NSZone *)zone { 26 | CFACustomAction *copyAction = [super copyWithZone:zone]; 27 | copyAction->_actionBlock = [_actionBlock copy]; 28 | 29 | return copyAction; 30 | } 31 | 32 | - (CFAAction *)reversedAction { 33 | return [self copy]; 34 | } 35 | 36 | - (void)executeWithTarget:(CALayer *)target forTime:(NSTimeInterval)time { 37 | NSCAssert(_actionBlock != NULL, @"CFACustomAction created with NULL custom action"); 38 | 39 | _actionBlock(target, time); 40 | self.finished = YES; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /CFAAction/CFAFade.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFAFade.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAAction+Private.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface CFAFade : CFAAction 15 | 16 | + (CFAAction *)fadeInWithDuration:(NSTimeInterval)sec; 17 | + (CFAAction *)fadeOutWithDuration:(NSTimeInterval)sec; 18 | + (CFAAction *)fadeAlphaBy:(CGFloat)factor duration:(NSTimeInterval)sec; 19 | + (CFAAction *)fadeAlphaTo:(CGFloat)alpha duration:(NSTimeInterval)sec; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /CFAAction/CFAFade.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFAFade.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAFade.h" 11 | 12 | static NSString *const CFAFadeKeypath = @"opacity"; 13 | 14 | typedef NS_ENUM(int, CFFadeSubtype) { 15 | CFFadeSubtypeNone = 0, 16 | CFFadeSubtypeDelta, 17 | CFFadeSubtypeDirect, 18 | CFFadeSubtypeIn, 19 | CFFadeSubtypeOut 20 | }; 21 | 22 | @implementation CFAFade { 23 | CGFloat _opacity; 24 | CFFadeSubtype _subtype; 25 | } 26 | 27 | + (CFAAction *)fadeInWithDuration:(NSTimeInterval)sec { 28 | CFAFade *fade = (CFAFade *)[CFAFade fadeAlphaTo:1.0f duration:sec]; 29 | fade->_subtype = CFFadeSubtypeIn; 30 | return fade; 31 | } 32 | 33 | + (CFAAction *)fadeOutWithDuration:(NSTimeInterval)sec { 34 | CFAFade *fade = (CFAFade *)[CFAFade fadeAlphaTo:0.0f duration:sec]; 35 | fade->_subtype = CFFadeSubtypeOut; 36 | return fade; 37 | } 38 | 39 | + (CFAAction *)fadeAlphaBy:(CGFloat)factor duration:(NSTimeInterval)sec { 40 | CFAFade *fadeAnimation = [[CFAFade alloc] init]; 41 | 42 | fadeAnimation->_opacity = factor; 43 | fadeAnimation->_subtype = CFFadeSubtypeDelta; 44 | fadeAnimation.duration = sec; 45 | 46 | return fadeAnimation; 47 | } 48 | 49 | + (CFAAction *)fadeAlphaTo:(CGFloat)alpha duration:(NSTimeInterval)sec { 50 | CFAFade *fadeAnimation = [[CFAFade alloc] init]; 51 | 52 | fadeAnimation->_opacity = alpha; 53 | fadeAnimation->_subtype = CFFadeSubtypeDirect; 54 | fadeAnimation.duration = sec; 55 | 56 | return fadeAnimation; 57 | } 58 | 59 | - (id)copyWithZone:(NSZone *)zone { 60 | CFAFade *copiedAction = [super copyWithZone:NULL]; 61 | copiedAction->_opacity = _opacity; 62 | copiedAction->_subtype = _subtype; 63 | copiedAction.duration = self.duration; 64 | return copiedAction; 65 | } 66 | 67 | - (CFAAction *)reversedAction { 68 | if (_subtype == CFFadeSubtypeDirect) { 69 | return [self copy]; 70 | } else if (_subtype == CFFadeSubtypeIn) { 71 | return [CFAFade fadeOutWithDuration:self.duration]; 72 | } else if (_subtype == CFFadeSubtypeOut) { 73 | return [CFAFade fadeInWithDuration:self.duration]; 74 | } 75 | CFAFade *copiedAction = [super copyWithZone:NULL]; 76 | copiedAction->_opacity = -_opacity; 77 | copiedAction->_subtype = _subtype; 78 | copiedAction.duration = self.duration; 79 | return copiedAction; 80 | } 81 | 82 | - (void)executeWithTarget:(CALayer *)target forTime:(NSTimeInterval)time { 83 | CGFloat newOpacity = [target.presentationLayer opacity]; 84 | switch (_subtype) { 85 | case CFFadeSubtypeDelta: 86 | newOpacity += _opacity; 87 | break; 88 | case CFFadeSubtypeOut: 89 | case CFFadeSubtypeIn: 90 | case CFFadeSubtypeDirect: 91 | newOpacity = _opacity; 92 | break; 93 | default: 94 | break; 95 | } 96 | 97 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:CFAFadeKeypath]; 98 | animation.removedOnCompletion = NO; 99 | animation.fillMode = kCAFillModeForwards; 100 | animation.beginTime = time; 101 | animation.fromValue = [target.presentationLayer ?: target valueForKeyPath:CFAFadeKeypath]; 102 | animation.toValue = @(newOpacity); 103 | animation.duration = self.duration; 104 | animation.timingFunction = self.timingFunction; 105 | [animation setDelegate:self]; 106 | [target.presentationLayer ?: target addAnimation:animation forKey:CFAFadeKeypath]; 107 | } 108 | 109 | - (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag { 110 | self.finished = YES; 111 | } 112 | 113 | @end 114 | -------------------------------------------------------------------------------- /CFAAction/CFAFollowPath.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFAFollowPath.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAAction+Private.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface CFAFollowPath : CFAAction 15 | 16 | + (CFAAction *)followPath:(CGPathRef)path duration:(NSTimeInterval)sec; 17 | + (CFAAction *)followPath:(CGPathRef)path asOffset:(BOOL)offset orientToPath:(BOOL)orient duration:(NSTimeInterval)sec; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /CFAAction/CFAFollowPath.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFAFollowPath.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAFollowPath.h" 11 | #ifdef __MAC_OS_X_VERSION_MAX_ALLOWED 12 | #import "NSBezierPath+CFABezierPathQuartzAssistant.h" 13 | #else 14 | #import 15 | #endif 16 | 17 | static NSString *const CFAFollowPathKeypath = @"position"; 18 | 19 | @implementation CFAFollowPath { 20 | CGPathRef _cgPath; 21 | } 22 | 23 | + (CFAAction *)followPath:(CGPathRef)path duration:(NSTimeInterval)sec { 24 | CFAFollowPath *pathFollowAction = [[CFAFollowPath alloc] init]; 25 | 26 | pathFollowAction->_cgPath = CGPathRetain(path); 27 | pathFollowAction.duration = sec; 28 | 29 | return pathFollowAction; 30 | } 31 | 32 | + (CFAAction *)followPath:(CGPathRef)path asOffset:(BOOL)offset orientToPath:(BOOL)orient duration:(NSTimeInterval)sec { 33 | CFAFollowPath *pathFollowAction = [[CFAFollowPath alloc] init]; 34 | 35 | pathFollowAction->_cgPath = CGPathRetain(path); 36 | pathFollowAction.duration = sec; 37 | 38 | return pathFollowAction; 39 | } 40 | 41 | - (id)copyWithZone:(NSZone *)zone { 42 | CFAFollowPath *copiedAction = [super copyWithZone:NULL]; 43 | copiedAction->_cgPath = CGPathRetain(_cgPath); 44 | copiedAction.duration = self.duration; 45 | return copiedAction; 46 | } 47 | 48 | - (CFAAction *)reversedAction { 49 | CFAFollowPath *reversedAction = [super copyWithZone:NULL]; 50 | #ifdef __MAC_OS_X_VERSION_MAX_ALLOWED 51 | NSBezierPath *path = [NSBezierPath bezierPath]; 52 | CGPathApply(_cgPath, (__bridge void *)path, CFAPathToBezierApplier); 53 | reversedAction->_cgPath = path.bezierPathByReversingPath.quartzPath; 54 | #else 55 | UIBezierPath *path = [UIBezierPath bezierPathWithCGPath:_cgPath]; 56 | reversedAction->_cgPath = CGPathRetain(path.bezierPathByReversingPath.CGPath); 57 | #endif 58 | reversedAction.duration = self.duration; 59 | return reversedAction; 60 | } 61 | 62 | - (void)dealloc { 63 | if (_cgPath) { 64 | CGPathRelease(_cgPath); 65 | } 66 | } 67 | 68 | - (void)executeWithTarget:(CALayer *)target forTime:(NSTimeInterval)time { 69 | CAKeyframeAnimation * pathAnimation = [CAKeyframeAnimation animationWithKeyPath:CFAFollowPathKeypath]; 70 | pathAnimation.path = _cgPath; 71 | pathAnimation.duration = self.duration; 72 | pathAnimation.calculationMode = kCAAnimationPaced; 73 | pathAnimation.beginTime = time; 74 | pathAnimation.removedOnCompletion = NO; 75 | pathAnimation.fillMode = kCAFillModeForwards; 76 | pathAnimation.timingFunction = self.timingFunction; 77 | [pathAnimation setDelegate:self]; 78 | [target addAnimation:pathAnimation forKey:CFAFollowPathKeypath]; 79 | } 80 | 81 | - (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag { 82 | self.finished = YES; 83 | } 84 | 85 | #ifdef __MAC_OS_X_VERSION_MAX_ALLOWED 86 | static void CFAPathToBezierApplier(void *info, const CGPathElement *element) { 87 | NSBezierPath *path = (__bridge NSBezierPath *)info; 88 | switch (element->type) { 89 | case kCGPathElementMoveToPoint: 90 | [path moveToPoint:element->points[0]]; 91 | break; 92 | 93 | case kCGPathElementAddLineToPoint: 94 | [path lineToPoint:element->points[0]]; 95 | break; 96 | 97 | case kCGPathElementAddQuadCurveToPoint: 98 | [path curveToPoint:element->points[1] controlPoint1:element->points[0] controlPoint2:element->points[0]]; 99 | break; 100 | 101 | case kCGPathElementAddCurveToPoint: 102 | [path curveToPoint:element->points[2] controlPoint1:element->points[0] controlPoint2:element->points[1]]; 103 | break; 104 | 105 | case kCGPathElementCloseSubpath: 106 | [path closePath]; 107 | break; 108 | } 109 | } 110 | #endif 111 | 112 | @end 113 | -------------------------------------------------------------------------------- /CFAAction/CFAGroup.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFAGroup.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/16/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAAction+Private.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface CFAGroup : CFAAction 15 | 16 | + (instancetype)groupWithActions:(NSArray *)actions; 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /CFAAction/CFAGroup.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFAGroup.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/16/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAGroup.h" 11 | #import "CFASequence.h" 12 | 13 | @implementation CFAGroup { 14 | NSArray *_actions; 15 | } 16 | 17 | - (id)init { 18 | self = [super init]; 19 | 20 | _actions = [[NSArray alloc] init]; 21 | 22 | return self; 23 | } 24 | 25 | + (instancetype)groupWithActions:(NSArray *)actions { 26 | CFAGroup *sequence = nil; 27 | if (actions != nil) { 28 | if (actions.count != 0) { 29 | NSMutableArray *groupedActions = [[NSMutableArray alloc] initWithCapacity:actions.count]; 30 | for (id action in actions) { 31 | if (![action isKindOfClass:NSArray.class]) { 32 | [groupedActions addObject:[action copy]]; 33 | } else { 34 | [groupedActions addObject:[CFASequence sequenceWithActions:action]]; 35 | } 36 | } 37 | sequence = [[CFAGroup alloc] init]; 38 | sequence->_actions = groupedActions.copy; 39 | NSTimeInterval accumulator = 0.f; 40 | for (CFAAction *group in groupedActions) { 41 | accumulator = MAX(accumulator, group.duration); 42 | } 43 | [sequence setDuration:accumulator]; 44 | } 45 | } 46 | return sequence; 47 | } 48 | 49 | - (id)copyWithZone:(NSZone *)zone { 50 | return [CFAGroup groupWithActions:_actions]; 51 | } 52 | 53 | - (CFAAction *)reversedAction { 54 | NSMutableArray *reversedActions = [NSMutableArray arrayWithCapacity:_actions.count]; 55 | for (CFAAction *action in _actions) { 56 | [reversedActions addObject:[action reversedAction]]; 57 | } 58 | return [CFAGroup groupWithActions:reversedActions]; 59 | } 60 | 61 | - (void)executeWithTarget:(CALayer *)target forTime:(NSTimeInterval)time { 62 | for (CFAAction *action in _actions) { 63 | [action executeWithTarget:target forTime:time]; 64 | } 65 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.duration * NSEC_PER_SEC)); 66 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 67 | self.finished = YES; 68 | }); 69 | } 70 | 71 | - (BOOL)finished { 72 | BOOL finished = YES; 73 | for (CFAAction *action in _actions) { 74 | if (!action.finished) return NO; 75 | } 76 | return finished; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /CFAAction/CFAHide.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFAHide.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 12/9/14. 6 | // Copyright (c) 2014 CodaFi. All rights reserved. 7 | // 8 | 9 | #import "CFAAction+Private.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface CFAHide : CFAAction 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /CFAAction/CFAHide.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFAHide.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 12/9/14. 6 | // Copyright (c) 2014 CodaFi. All rights reserved. 7 | // 8 | 9 | #import "CFAHide.h" 10 | 11 | @implementation CFAHide { 12 | BOOL _willHideNode; 13 | } 14 | 15 | + (CFAAction *)hide { 16 | CFAHide *hideAction = [[CFAHide alloc] init]; 17 | hideAction->_willHideNode = YES; 18 | return hideAction; 19 | } 20 | 21 | + (CFAAction *)unhide { 22 | CFAHide *hideAction = [[CFAHide alloc] init]; 23 | hideAction->_willHideNode = NO; 24 | return hideAction; 25 | } 26 | 27 | - (id)copyWithZone:(NSZone *)zone { 28 | CFAHide *copiedAction = [super copyWithZone:zone]; 29 | copiedAction->_willHideNode = _willHideNode; 30 | return copiedAction; 31 | } 32 | 33 | - (CFAAction *)reversedAction { 34 | CFAHide *copiedAction = [super copyWithZone:NULL]; 35 | copiedAction->_willHideNode = !_willHideNode; 36 | return copiedAction; 37 | } 38 | 39 | - (void)executeWithTarget:(CALayer *)target forTime:(NSTimeInterval)time { 40 | target.hidden = _willHideNode; 41 | self.finished = YES; 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /CFAAction/CFAMove.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFAMove.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/16/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAAction+Private.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface CFAMove : CFAAction 15 | 16 | + (CFAAction *)moveByX:(CGFloat)deltaX y:(CGFloat)deltaY duration:(NSTimeInterval)sec; 17 | #if CGVECTOR_DEFINED 18 | + (CFAAction *)moveBy:(CGVector)delta duration:(NSTimeInterval)sec; 19 | #endif 20 | + (CFAAction *)moveTo:(CGPoint)location duration:(NSTimeInterval)sec; 21 | + (CFAAction *)moveToX:(CGFloat)x duration:(NSTimeInterval)sec; 22 | + (CFAAction *)moveToY:(CGFloat)y duration:(NSTimeInterval)sec; 23 | 24 | @end 25 | 26 | NS_ASSUME_NONNULL_END 27 | -------------------------------------------------------------------------------- /CFAAction/CFAMove.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFAMove.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/16/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAMove.h" 11 | #ifdef __IPHONE_OS_VERSION_MIN_REQUIRED 12 | #import 13 | #endif 14 | 15 | static NSString *const CFAMoveKeypath = @"position"; 16 | 17 | typedef NS_ENUM(int, CFMoveSubtype) { 18 | CFMoveSubtypeNone = 0, 19 | CFMoveSubtypeDelta, 20 | CFMoveSubtypeDirect 21 | }; 22 | 23 | @implementation CFAMove { 24 | double x, y; 25 | CFMoveSubtype _subtype; 26 | } 27 | 28 | #if CGVECTOR_DEFINED 29 | + (CFAAction *)moveBy:(CGVector)delta duration:(NSTimeInterval)sec { 30 | return [CFAMove moveByX:delta.dx y:delta.dy duration:sec]; 31 | } 32 | #endif 33 | 34 | + (CFAAction *)moveByX:(CGFloat)deltaX y:(CGFloat)deltaY duration:(NSTimeInterval)sec { 35 | CFAMove *moveAction = [[CFAMove alloc] init]; 36 | 37 | moveAction->x = deltaX; 38 | moveAction->y = deltaY; 39 | moveAction->_subtype = CFMoveSubtypeDelta; 40 | moveAction.duration = sec; 41 | 42 | return moveAction; 43 | } 44 | 45 | + (CFAAction *)moveTo:(CGPoint)location duration:(NSTimeInterval)sec { 46 | return [CFAMove moveToX:location.x y:location.y duration:sec]; 47 | } 48 | 49 | + (CFAAction *)moveToX:(CGFloat)x duration:(NSTimeInterval)sec { 50 | return [CFAMove moveToX:x y:0.0f duration:sec]; 51 | } 52 | 53 | + (CFAAction *)moveToY:(CGFloat)y duration:(NSTimeInterval)sec { 54 | return [CFAMove moveToX:0.0f y:y duration:sec]; 55 | } 56 | 57 | + (CFAAction *)moveToX:(CGFloat)x y:(CGFloat)y duration:(NSTimeInterval)sec { 58 | CFAMove *moveAction = [[CFAMove alloc] init]; 59 | 60 | moveAction->x = x; 61 | moveAction->y = y; 62 | moveAction.duration = sec; 63 | moveAction->_subtype = CFMoveSubtypeDirect; 64 | 65 | return moveAction; 66 | } 67 | 68 | - (id)copyWithZone:(NSZone *)zone { 69 | CFAMove *copiedAction = [super copyWithZone:zone]; 70 | copiedAction->x = x; 71 | copiedAction->y = y; 72 | copiedAction->_subtype = _subtype; 73 | copiedAction.duration = self.duration; 74 | return copiedAction; 75 | } 76 | 77 | - (CFAAction *)reversedAction { 78 | if (_subtype == CFMoveSubtypeDirect) { 79 | return [self copy]; 80 | } 81 | CFAMove *copiedAction = [super copyWithZone:NULL]; 82 | copiedAction->x = -x; 83 | copiedAction->y = -y; 84 | copiedAction->_subtype = _subtype; 85 | copiedAction.duration = self.duration; 86 | return copiedAction; 87 | } 88 | 89 | - (void)executeWithTarget:(CALayer *)target forTime:(NSTimeInterval)time { 90 | CGPoint newPosition = [(CALayer *)target.presentationLayer ?: target position]; 91 | switch (_subtype) { 92 | case CFMoveSubtypeDelta: 93 | newPosition.x += x; 94 | newPosition.y += y; 95 | break; 96 | case CFMoveSubtypeDirect: 97 | newPosition.x = x; 98 | newPosition.y = y; 99 | break; 100 | default: 101 | break; 102 | } 103 | 104 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:CFAMoveKeypath]; 105 | animation.removedOnCompletion = NO; 106 | animation.fillMode = kCAFillModeForwards; 107 | animation.beginTime = time; 108 | animation.fromValue = [(CALayer *)target.presentationLayer ?: target valueForKey:CFAMoveKeypath]; 109 | #ifdef __IPHONE_OS_VERSION_MIN_REQUIRED 110 | animation.toValue = [NSValue valueWithCGPoint:newPosition]; 111 | #else 112 | animation.toValue = [NSValue valueWithPoint:newPosition]; 113 | #endif 114 | animation.duration = self.duration; 115 | animation.repeatCount = self.repeatCount; 116 | animation.cumulative = YES; 117 | animation.timingFunction = self.timingFunction; 118 | [animation setDelegate:self]; 119 | [target.presentationLayer ?: target addAnimation:animation forKey:CFAMoveKeypath]; 120 | } 121 | 122 | - (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag { 123 | self.finished = YES; 124 | } 125 | 126 | @end 127 | -------------------------------------------------------------------------------- /CFAAction/CFAPerformSelector.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFAPerformSelector.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAAction+Private.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface CFAPerformSelector : CFAAction 15 | 16 | + (CFAAction *)performSelector:(SEL)selector onTarget:(id)target; 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /CFAAction/CFAPerformSelector.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFAPerformSelector.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAPerformSelector.h" 11 | #include 12 | 13 | @implementation CFAPerformSelector { 14 | SEL _selector; 15 | id _target; 16 | } 17 | 18 | + (CFAAction *)performSelector:(SEL)selector onTarget:(id)target { 19 | CFAPerformSelector *action = [[CFAPerformSelector alloc] init]; 20 | 21 | action->_selector = selector; 22 | action->_target = target; 23 | 24 | return action; 25 | } 26 | 27 | - (void)executeWithTarget:(CALayer *)target forTime:(NSTimeInterval)time { 28 | if (!self.finished) { 29 | if ((_target != nil) && (_selector != NULL)) { 30 | void(*cfa_msgSend)(id self, SEL _cmd) = (void *)objc_msgSend; 31 | cfa_msgSend(_target, _selector); 32 | } 33 | self.finished = YES; 34 | } 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /CFAAction/CFARepeat.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFARepeat.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAAction+Private.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface CFARepeat : CFAAction 15 | 16 | + (CFAAction *)repeatAction:(CFAAction *)action count:(NSUInteger)count; 17 | + (CFAAction *)repeatActionForever:(CFAAction *)action; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /CFAAction/CFARepeat.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFARepeat.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFARepeat.h" 11 | 12 | @implementation CFARepeat { 13 | CFAAction *_repeatedAction; 14 | float _count; 15 | BOOL _forever; 16 | } 17 | 18 | + (CFAAction *)repeatAction:(CFAAction *)action count:(NSUInteger)count { 19 | NSParameterAssert(action != nil); 20 | 21 | CFARepeat *repeatAction = [[CFARepeat alloc] init]; 22 | 23 | repeatAction->_repeatedAction = [action copy]; 24 | repeatAction->_count = count; 25 | repeatAction->_forever = NO; 26 | repeatAction.duration = action.duration * ((count < 1) ? 1 : count); 27 | 28 | return repeatAction; 29 | } 30 | 31 | + (CFAAction *)repeatActionForever:(CFAAction *)action { 32 | NSParameterAssert(action != nil); 33 | 34 | CFARepeat *repeatAction = [[CFARepeat alloc] init]; 35 | 36 | repeatAction->_repeatedAction = [action copy]; 37 | repeatAction->_count = HUGE_VALF; 38 | repeatAction.duration = action.duration * HUGE_VALF; 39 | 40 | return repeatAction; 41 | } 42 | 43 | - (id)copyWithZone:(NSZone *)zone { 44 | CFARepeat *repeatedCopy = nil; 45 | 46 | if (_forever) { 47 | repeatedCopy = (CFARepeat *)[CFARepeat repeatActionForever:_repeatedAction]; 48 | } else { 49 | repeatedCopy = (CFARepeat *)[CFARepeat repeatAction:_repeatedAction count:_count]; 50 | } 51 | 52 | return repeatedCopy; 53 | } 54 | 55 | - (CFAAction *)reversedAction { 56 | return [self copy]; 57 | } 58 | 59 | - (void)executeWithTarget:(CALayer *)target forTime:(NSTimeInterval)time { 60 | _repeatedAction.repeatCount = _count; 61 | [_repeatedAction executeWithTarget:target forTime:time]; 62 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.duration * NSEC_PER_SEC)); 63 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 64 | self.finished = YES; 65 | }); 66 | } 67 | 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /CFAAction/CFAResize.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFAResize.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAAction+Private.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface CFAResize : CFAAction 15 | 16 | + (CFAAction *)resizeByWidth:(CGFloat)width height:(CGFloat)height duration:(NSTimeInterval)duration; 17 | + (CFAAction *)resizeToWidth:(CGFloat)width height:(CGFloat)height duration:(NSTimeInterval)duration; 18 | + (CFAAction *)resizeToWidth:(CGFloat)width duration:(NSTimeInterval)duration; 19 | + (CFAAction *)resizeToHeight:(CGFloat)height duration:(NSTimeInterval)duration; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /CFAAction/CFAResize.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFAResize.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAResize.h" 11 | #ifdef __IPHONE_OS_VERSION_MIN_REQUIRED 12 | #import 13 | #endif 14 | 15 | static NSString *const CFAResizeKeypath = @"bounds"; 16 | 17 | typedef NS_ENUM(int, CFResizeSubtype) { 18 | CFResizeSubtypeNone = 0, 19 | CFResizeSubtypeDelta, 20 | CFResizeSubtypeDirect 21 | }; 22 | 23 | @implementation CFAResize { 24 | double width, height; 25 | CFResizeSubtype _subtype; 26 | } 27 | 28 | + (CFAAction *)resizeByWidth:(CGFloat)width height:(CGFloat)height duration:(NSTimeInterval)duration { 29 | CFAResize *resizeAction = [[CFAResize alloc] init]; 30 | 31 | resizeAction->width = width; 32 | resizeAction->height = height; 33 | resizeAction.duration = duration; 34 | resizeAction->_subtype = CFResizeSubtypeDelta; 35 | 36 | return resizeAction; 37 | } 38 | 39 | + (CFAAction *)resizeToWidth:(CGFloat)width height:(CGFloat)height duration:(NSTimeInterval)duration { 40 | CFAResize *resizeAction = [[CFAResize alloc] init]; 41 | 42 | resizeAction->width = width; 43 | resizeAction->height = height; 44 | resizeAction.duration = duration; 45 | resizeAction->_subtype = CFResizeSubtypeDirect; 46 | 47 | return resizeAction; 48 | } 49 | 50 | + (CFAAction *)resizeToWidth:(CGFloat)width duration:(NSTimeInterval)duration { 51 | return [CFAResize resizeToWidth:width height:MAXFLOAT duration:duration]; 52 | } 53 | 54 | + (CFAAction *)resizeToHeight:(CGFloat)height duration:(NSTimeInterval)duration { 55 | return [CFAResize resizeToWidth:MAXFLOAT height:height duration:duration]; 56 | } 57 | 58 | - (id)copyWithZone:(NSZone *)zone { 59 | CFAResize *copiedAction = [super copyWithZone:zone]; 60 | copiedAction->width = width; 61 | copiedAction->height = height; 62 | copiedAction->_subtype = _subtype; 63 | copiedAction.duration = self.duration; 64 | return copiedAction; 65 | } 66 | 67 | - (CFAAction *)reversedAction { 68 | if (_subtype == CFResizeSubtypeDirect) { 69 | return [self copy]; 70 | } 71 | CFAResize *copiedAction = [super copyWithZone:NULL]; 72 | copiedAction->width = -width; 73 | copiedAction->height = -height; 74 | copiedAction->_subtype = _subtype; 75 | return copiedAction; 76 | } 77 | 78 | - (void)executeWithTarget:(CALayer *)target forTime:(NSTimeInterval)time { 79 | CGRect newFrame = [target.presentationLayer ?:target bounds]; 80 | switch (_subtype) { 81 | case CFResizeSubtypeDelta: 82 | if (width != MAXFLOAT) 83 | newFrame.size.width += width; 84 | if (height != MAXFLOAT) 85 | newFrame.size.height += height; 86 | break; 87 | case CFResizeSubtypeDirect: 88 | if (width != MAXFLOAT) 89 | newFrame.size.width = width; 90 | if (height != MAXFLOAT) 91 | newFrame.size.height = height; 92 | break; 93 | default: 94 | break; 95 | } 96 | 97 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:CFAResizeKeypath]; 98 | animation.fillMode = kCAFillModeForwards; 99 | animation.beginTime = time; 100 | animation.removedOnCompletion = NO; 101 | #ifdef __IPHONE_OS_VERSION_MIN_REQUIRED 102 | animation.fromValue = [NSValue valueWithCGRect:[target.presentationLayer ?:target bounds]]; 103 | animation.toValue = [NSValue valueWithCGRect:newFrame]; 104 | #else 105 | animation.fromValue = [NSValue valueWithRect:[target.presentationLayer ?:target bounds]]; 106 | animation.toValue = [NSValue valueWithRect:newFrame]; 107 | #endif 108 | animation.duration = self.duration; 109 | animation.repeatCount = self.repeatCount; 110 | animation.cumulative = YES; 111 | animation.timingFunction = self.timingFunction; 112 | [target.presentationLayer ?:target addAnimation:animation forKey:CFAResizeKeypath]; 113 | } 114 | 115 | - (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag { 116 | self.finished = YES; 117 | } 118 | 119 | @end 120 | 121 | -------------------------------------------------------------------------------- /CFAAction/CFARotate.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFARotate.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/16/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAAction+Private.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface CFARotate : CFAAction 15 | 16 | + (CFAAction *)rotateByAngle:(CGFloat)radians duration:(NSTimeInterval)sec; 17 | + (CFAAction *)rotateToAngle:(CGFloat)radians duration:(NSTimeInterval)sec; 18 | + (CFAAction *)rotateToAngle:(CGFloat)radians duration:(NSTimeInterval)sec shortestUnitArc:(BOOL)shortestUnitArc; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /CFAAction/CFARotate.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFARotate.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/16/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFARotate.h" 11 | 12 | static NSString *const CFARotateKeypath = @"transform.rotation"; 13 | 14 | typedef NS_ENUM(int, CFRotateSubtype) { 15 | CFRotateSubtypeNone = 0, 16 | CFRotateSubtypeDelta, 17 | CFRotateSubtypeDirect 18 | }; 19 | 20 | @implementation CFARotate { 21 | CGFloat _angle; 22 | CFRotateSubtype _subtype; 23 | } 24 | 25 | + (CFAAction *)rotateByAngle:(CGFloat)radians duration:(NSTimeInterval)sec { 26 | CFARotate *resizeAction = [[CFARotate alloc] init]; 27 | 28 | resizeAction->_angle = radians; 29 | resizeAction.duration = sec; 30 | resizeAction->_subtype = CFRotateSubtypeDelta; 31 | 32 | return resizeAction; 33 | } 34 | 35 | + (CFAAction *)rotateToAngle:(CGFloat)radians duration:(NSTimeInterval)sec { 36 | return [CFARotate rotateToAngle:radians duration:sec shortestUnitArc:NO]; 37 | } 38 | 39 | + (CFAAction *)rotateToAngle:(CGFloat)radians duration:(NSTimeInterval)sec shortestUnitArc:(BOOL)shortestUnitArc { 40 | CFARotate *resizeAction = [[CFARotate alloc] init]; 41 | 42 | resizeAction->_angle = radians; 43 | resizeAction.duration = sec; 44 | resizeAction->_subtype = CFRotateSubtypeDirect; 45 | 46 | return resizeAction; 47 | } 48 | 49 | - (id)copyWithZone:(NSZone *)zone { 50 | CFARotate *copiedAction = [super copyWithZone:zone]; 51 | copiedAction->_angle = _angle; 52 | copiedAction->_subtype = _subtype; 53 | copiedAction.duration = self.duration; 54 | return copiedAction; 55 | } 56 | 57 | - (CFAAction *)reversedAction { 58 | if (_subtype == CFRotateSubtypeDirect) { 59 | return [self copy]; 60 | } 61 | CFARotate *copiedAction = [super copyWithZone:NULL]; 62 | copiedAction->_angle = -_angle; 63 | copiedAction->_subtype = _subtype; 64 | copiedAction.duration = self.duration; 65 | return copiedAction; 66 | } 67 | 68 | - (void)executeWithTarget:(CALayer *)target forTime:(NSTimeInterval)time { 69 | CGFloat angle = [(NSNumber *)[target.presentationLayer valueForKeyPath:@"transform.rotation"] floatValue]; 70 | switch (_subtype) { 71 | case CFRotateSubtypeDelta: 72 | angle += _angle; 73 | break; 74 | case CFRotateSubtypeDirect: 75 | angle = _angle; 76 | break; 77 | default: 78 | break; 79 | } 80 | 81 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:CFARotateKeypath]; 82 | animation.removedOnCompletion = NO; 83 | animation.fillMode = kCAFillModeForwards; 84 | animation.beginTime = time; 85 | animation.fromValue = [target.presentationLayer valueForKeyPath:@"transform.rotation"]; 86 | animation.toValue = @(_angle); 87 | animation.duration = self.duration; 88 | animation.repeatCount = self.repeatCount; 89 | animation.cumulative = YES; 90 | animation.timingFunction = self.timingFunction; 91 | [animation setDelegate:self]; 92 | [target.presentationLayer ?: target addAnimation:animation forKey:CFARotateKeypath]; 93 | } 94 | 95 | - (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag { 96 | self.finished = YES; 97 | } 98 | 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /CFAAction/CFARunBlock.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFARunBlock.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/16/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAAction+Private.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface CFARunBlock : CFAAction 15 | 16 | + (CFAAction *)runBlock:(dispatch_block_t)block; 17 | + (CFAAction *)runBlock:(dispatch_block_t)block queue:(dispatch_queue_t)queue; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /CFAAction/CFARunBlock.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFARunBlock.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/16/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFARunBlock.h" 11 | 12 | @implementation CFARunBlock { 13 | dispatch_block_t _block; 14 | dispatch_queue_t _queue; 15 | } 16 | 17 | + (CFAAction *)runBlock:(dispatch_block_t)block { 18 | return [CFAAction runBlock:block queue:dispatch_get_main_queue()]; 19 | } 20 | 21 | + (CFAAction *)runBlock:(dispatch_block_t)block queue:(dispatch_queue_t)queue { 22 | CFARunBlock *runBlock = [[CFARunBlock alloc] init]; 23 | runBlock->_block = (__bridge dispatch_block_t)Block_copy((__bridge void *)block); 24 | runBlock->_queue = queue; 25 | return runBlock; 26 | } 27 | 28 | - (id)copyWithZone:(NSZone *)zone { 29 | CFARunBlock *copiedAction = [super copyWithZone:zone]; 30 | copiedAction->_block = (__bridge dispatch_block_t)Block_copy((__bridge void *)_block); 31 | copiedAction->_queue = _queue; 32 | return copiedAction; 33 | } 34 | 35 | - (CFAAction *)reversedAction { 36 | return [self copy]; 37 | } 38 | 39 | - (void)executeWithTarget:(id)target forTime:(NSTimeInterval)time { 40 | if (self.finished) { 41 | return; 42 | } 43 | dispatch_async(_queue, _block); 44 | self.finished = YES; 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /CFAAction/CFAScale.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFAScale.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/16/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAAction+Private.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface CFAScale : CFAAction 15 | 16 | + (CFAAction *)scaleBy:(CGFloat)scale duration:(NSTimeInterval)sec; 17 | + (CFAAction *)scaleXBy:(CGFloat)xScale y:(CGFloat)yScale duration:(NSTimeInterval)sec; 18 | + (CFAAction *)scaleTo:(CGFloat)scale duration:(NSTimeInterval)sec; 19 | + (CFAAction *)scaleXTo:(CGFloat)xScale y:(CGFloat)yScale duration:(NSTimeInterval)sec; 20 | + (CFAAction *)scaleXTo:(CGFloat)scale duration:(NSTimeInterval)sec; 21 | + (CFAAction *)scaleYTo:(CGFloat)scale duration:(NSTimeInterval)sec; 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /CFAAction/CFAScale.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFAScale.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/16/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAScale.h" 11 | 12 | static NSString *const CFAScaleKeypath = @"scale"; 13 | static NSString *const CFAScaleXKeypath = @"transform.scale.x"; 14 | static NSString *const CFAScaleYKeypath = @"transform.scale.y"; 15 | 16 | typedef NS_ENUM(int, CFScaleSubtype) { 17 | CFScaleSubtypeNone = 0, 18 | CFScaleSubtypeDelta, 19 | CFScaleSubtypeDirect 20 | }; 21 | 22 | @implementation CFAScale { 23 | CGFloat x, y; 24 | CFScaleSubtype _subtype; 25 | } 26 | 27 | + (CFAAction *)scaleBy:(CGFloat)scale duration:(NSTimeInterval)sec { 28 | return [CFAScale scaleXBy:scale y:scale duration:sec]; 29 | } 30 | 31 | + (CFAAction *)scaleXBy:(CGFloat)xScale y:(CGFloat)yScale duration:(NSTimeInterval)sec { 32 | CFAScale *scaleAnimation = [[CFAScale alloc] init]; 33 | 34 | scaleAnimation->x = xScale; 35 | scaleAnimation->y = yScale; 36 | scaleAnimation->_subtype = CFScaleSubtypeDelta; 37 | scaleAnimation.duration = sec; 38 | 39 | return scaleAnimation; 40 | } 41 | 42 | + (CFAAction *)scaleTo:(CGFloat)scale duration:(NSTimeInterval)sec { 43 | return [CFAScale scaleXTo:scale y:scale duration:sec]; 44 | } 45 | 46 | + (CFAAction *)scaleXTo:(CGFloat)xScale y:(CGFloat)yScale duration:(NSTimeInterval)sec { 47 | CFAScale *scaleAnimation = [[CFAScale alloc] init]; 48 | 49 | scaleAnimation->x = xScale; 50 | scaleAnimation->y = yScale; 51 | scaleAnimation->_subtype = CFScaleSubtypeDirect; 52 | scaleAnimation.duration = sec; 53 | 54 | return scaleAnimation; 55 | } 56 | 57 | + (CFAAction *)scaleXTo:(CGFloat)scale duration:(NSTimeInterval)sec { 58 | return [CFAScale scaleXBy:scale y:MAXFLOAT duration:sec]; 59 | } 60 | 61 | + (CFAAction *)scaleYTo:(CGFloat)scale duration:(NSTimeInterval)sec { 62 | return [CFAScale scaleXBy:MAXFLOAT y:scale duration:sec]; 63 | } 64 | 65 | - (id)copyWithZone:(NSZone *)zone { 66 | CFAScale *copiedAction = [super copyWithZone:zone]; 67 | copiedAction->x = x; 68 | copiedAction->y = y; 69 | copiedAction->_subtype = _subtype; 70 | return copiedAction; 71 | } 72 | 73 | - (CFAAction *)reversedAction { 74 | if (_subtype == CFScaleSubtypeDirect) { 75 | return [self copy]; 76 | } 77 | CFAScale *copiedAction = [super copyWithZone:NULL]; 78 | copiedAction->x = -y; 79 | copiedAction->y = -x; 80 | copiedAction->_subtype = _subtype; 81 | return copiedAction; 82 | } 83 | 84 | - (void)executeWithTarget:(CALayer *)target forTime:(NSTimeInterval)time { 85 | float newX = CATransform3DGetAffineTransform(target.transform).a; 86 | float newY = CATransform3DGetAffineTransform(target.transform).b; 87 | 88 | switch (_subtype) { 89 | case CFScaleSubtypeDelta: 90 | if (x != MAXFLOAT) { 91 | newX += x; 92 | } 93 | if (y != MAXFLOAT) { 94 | newY += y; 95 | } 96 | break; 97 | case CFScaleSubtypeDirect: 98 | if (x != MAXFLOAT) { 99 | newX = x; 100 | } 101 | if (y != MAXFLOAT) { 102 | newY = y; 103 | } 104 | break; 105 | default: 106 | break; 107 | } 108 | 109 | CABasicAnimation *scaleX = [CABasicAnimation animationWithKeyPath:@"transform.scale.x"]; 110 | scaleX.autoreverses = NO; 111 | scaleX.fromValue = [target.presentationLayer valueForKey:@"transform.scale.x"]; 112 | scaleX.toValue = [NSNumber numberWithFloat:newX]; 113 | scaleX.fillMode = kCAFillModeForwards; 114 | scaleX.removedOnCompletion = NO; 115 | scaleX.beginTime = time; 116 | scaleX.cumulative = YES; 117 | scaleX.timingFunction = self.timingFunction; 118 | 119 | CABasicAnimation *scaleY = [CABasicAnimation animationWithKeyPath:@"transform.scale.y"]; 120 | scaleY.autoreverses = NO; 121 | scaleY.fromValue = [target.presentationLayer valueForKey:@"transform.scale.y"]; 122 | scaleY.toValue = [NSNumber numberWithFloat:newY]; 123 | scaleY.fillMode = kCAFillModeForwards; 124 | scaleY.removedOnCompletion = NO; 125 | scaleY.beginTime = time; 126 | scaleY.cumulative = YES; 127 | scaleY.timingFunction = self.timingFunction; 128 | 129 | [target addAnimation:scaleX forKey:CFAScaleXKeypath]; 130 | [target addAnimation:scaleY forKey:CFAScaleYKeypath]; 131 | } 132 | 133 | @end 134 | -------------------------------------------------------------------------------- /CFAAction/CFASequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFASequence.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/16/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAAction+Private.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface CFASequence : CFAAction 15 | 16 | + (instancetype)sequenceWithActions:(NSArray *)actions; 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /CFAAction/CFASequence.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFASequence.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/16/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFASequence.h" 11 | #import "CFAGroup.h" 12 | 13 | @implementation CFASequence { 14 | NSArray *_actions; 15 | } 16 | 17 | - (id)init { 18 | self = [super init]; 19 | 20 | _actions = [[NSArray alloc] init]; 21 | 22 | return self; 23 | } 24 | 25 | + (instancetype)sequenceWithActions:(NSArray *)actions { 26 | CFASequence *sequence = nil; 27 | if (actions != nil) { 28 | if (actions.count != 0) { 29 | NSMutableArray *groupedActions = [[NSMutableArray alloc] initWithCapacity:actions.count]; 30 | for (id action in actions) { 31 | if (![action isKindOfClass:NSArray.class]) { 32 | [groupedActions addObject:[action copy]]; 33 | } else { 34 | [groupedActions addObject:[CFAGroup groupWithActions:action]]; 35 | } 36 | } 37 | sequence = [[CFASequence alloc] init]; 38 | sequence->_actions = groupedActions.copy; 39 | NSTimeInterval accumulator = 0.f; 40 | for (CFAGroup *group in groupedActions) { 41 | accumulator += group.duration; 42 | } 43 | [sequence setDuration:accumulator]; 44 | } 45 | } 46 | return sequence; 47 | } 48 | 49 | - (id)copyWithZone:(NSZone *)zone { 50 | return [CFASequence sequenceWithActions:_actions]; 51 | } 52 | 53 | - (void)executeWithTarget:(CALayer *)target forTime:(NSTimeInterval)time { 54 | NSTimeInterval accumulator = 0.0f; 55 | for (CFAAction *action in _actions) { 56 | [action executeWithTarget:target forTime:time + accumulator]; 57 | accumulator += action.duration; 58 | } 59 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(accumulator * NSEC_PER_SEC)); 60 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 61 | self.finished = YES; 62 | }); 63 | } 64 | 65 | - (CFAAction *)reversedAction { 66 | NSMutableArray *reversedActions = [NSMutableArray arrayWithCapacity:_actions.count]; 67 | for (CFAAction *action in _actions) { 68 | [reversedActions insertObject:[action reversedAction] atIndex:0]; 69 | } 70 | return [CFASequence sequenceWithActions:reversedActions]; 71 | } 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /CFAAction/CFASpeed.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFASpeed.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAAction+Private.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface CFASpeed : CFAAction 15 | 16 | + (CFAAction *)speedBy:(CGFloat)speed duration:(NSTimeInterval)sec; 17 | + (CFAAction *)speedTo:(CGFloat)speed duration:(NSTimeInterval)sec; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /CFAAction/CFASpeed.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFASpeed.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFASpeed.h" 11 | 12 | static NSString *const CFASpeedKeypath = @"speed"; 13 | 14 | typedef NS_ENUM(int, CFSpeedSubtype) { 15 | CFSpeedSubtypeNone = 0, 16 | CFSpeedSubtypeDelta, 17 | CFSpeedSubtypeDirect 18 | }; 19 | 20 | @implementation CFASpeed { 21 | CGFloat _speed; 22 | CFSpeedSubtype _subtype; 23 | } 24 | 25 | + (CFAAction *)speedBy:(CGFloat)speed duration:(NSTimeInterval)sec { 26 | CFASpeed *speedAnimation = [[CFASpeed alloc] init]; 27 | 28 | speedAnimation->_speed = speed; 29 | speedAnimation->_subtype = CFSpeedSubtypeDelta; 30 | speedAnimation.duration = sec; 31 | 32 | return speedAnimation; 33 | } 34 | 35 | + (CFAAction *)speedTo:(CGFloat)speed duration:(NSTimeInterval)sec { 36 | CFASpeed *speedAnimation = [[CFASpeed alloc] init]; 37 | 38 | speedAnimation->_speed = speed; 39 | speedAnimation->_subtype = CFSpeedSubtypeDirect; 40 | speedAnimation.duration = sec; 41 | 42 | return speedAnimation; 43 | } 44 | 45 | - (id)copyWithZone:(NSZone *)zone { 46 | CFASpeed *copiedAction = [super copyWithZone:NULL]; 47 | copiedAction->_speed = _speed; 48 | copiedAction->_subtype = _subtype; 49 | copiedAction.duration = self.duration; 50 | return copiedAction; 51 | } 52 | 53 | - (CFAAction *)reversedAction { 54 | CFASpeed *copiedAction = [super copyWithZone:NULL]; 55 | copiedAction->_speed = -_speed; 56 | copiedAction->_subtype = _subtype; 57 | copiedAction.duration = self.duration; 58 | return copiedAction; 59 | } 60 | 61 | - (void)executeWithTarget:(CALayer *)target forTime:(NSTimeInterval)time { 62 | CGFloat newSpeed = [(CALayer *)target.presentationLayer ?: target speed]; 63 | 64 | switch (_subtype) { 65 | case CFSpeedSubtypeDelta: 66 | newSpeed += _speed; 67 | break; 68 | case CFSpeedSubtypeDirect: 69 | newSpeed = _speed; 70 | break; 71 | default: 72 | break; 73 | } 74 | 75 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; 76 | animation.removedOnCompletion = NO; 77 | animation.fillMode = kCAFillModeForwards; 78 | animation.beginTime = time; 79 | animation.fromValue = [(CALayer *)target.presentationLayer ?: target valueForKey:CFASpeedKeypath]; 80 | animation.toValue = @(newSpeed); 81 | animation.duration = self.duration; 82 | animation.timingFunction = self.timingFunction; 83 | [animation setDelegate:self]; 84 | [target.presentationLayer ?: target addAnimation:animation forKey:CFASpeedKeypath]; 85 | } 86 | 87 | - (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag { 88 | self.finished = YES; 89 | } 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /CFAAction/CFAWait.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFAWait.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAAction+Private.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface CFAWait : CFAAction 15 | 16 | + (CFAAction *)waitForDuration:(NSTimeInterval)sec; 17 | + (CFAAction *)waitForDuration:(NSTimeInterval)sec withRange:(NSTimeInterval)durationRange; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /CFAAction/CFAWait.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFAWait.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import "CFAWait.h" 11 | 12 | @implementation CFAWait { 13 | NSTimeInterval _range; 14 | } 15 | 16 | + (CFAAction *)waitForDuration:(NSTimeInterval)sec { 17 | CFAWait *waitAnimation = [[CFAWait alloc] init]; 18 | 19 | waitAnimation.duration = sec; 20 | 21 | return waitAnimation; 22 | } 23 | 24 | + (CFAAction *)waitForDuration:(NSTimeInterval)sec withRange:(NSTimeInterval)durationRange { 25 | CFAWait *waitAnimation = [[CFAWait alloc] init]; 26 | 27 | waitAnimation->_range = durationRange; 28 | waitAnimation.duration = sec; 29 | 30 | return waitAnimation; 31 | } 32 | 33 | - (id)copyWithZone:(NSZone *)zone { 34 | CFAWait *copiedAction = [super copyWithZone:NULL]; 35 | copiedAction->_range = _range; 36 | copiedAction.duration = self.duration; 37 | return copiedAction; 38 | } 39 | 40 | - (CFAAction *)reversedAction { 41 | return [self copy]; 42 | } 43 | 44 | - (void)executeWithTarget:(CALayer *)target forTime:(NSTimeInterval)time { 45 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.duration * NSEC_PER_SEC)); 46 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 47 | self.finished = YES; 48 | }); 49 | } 50 | 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /CFAAction/NSBezierPath+CFABezierPathQuartzAssistant.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSBezierPath+CFABezierPathQuartzAssistant.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/18/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import 11 | 12 | #if !(TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR) 13 | 14 | #import 15 | 16 | @interface NSBezierPath (CFABezierPathQuartzAssistant) 17 | 18 | - (CGPathRef)quartzPath; 19 | 20 | @end 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /CFAAction/NSBezierPath+CFABezierPathQuartzAssistant.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSBezierPath+CFABezierPathQuartzAssistant.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/18/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | #if !(TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR) 11 | 12 | #import "NSBezierPath+CFABezierPathQuartzAssistant.h" 13 | 14 | @implementation NSBezierPath (BezierPathQuartzUtilities) 15 | // This method works only in OS X v10.2 and later. 16 | - (CGPathRef)quartzPath 17 | { 18 | NSInteger i, numElements; 19 | 20 | // Need to begin a path here. 21 | CGPathRef immutablePath = NULL; 22 | 23 | // Then draw the path elements. 24 | numElements = [self elementCount]; 25 | if (numElements > 0) 26 | { 27 | CGMutablePathRef path = CGPathCreateMutable(); 28 | NSPoint points[3]; 29 | BOOL didClosePath = YES; 30 | 31 | for (i = 0; i < numElements; i++) 32 | { 33 | switch ([self elementAtIndex:i associatedPoints:points]) 34 | { 35 | case NSMoveToBezierPathElement: 36 | CGPathMoveToPoint(path, NULL, points[0].x, points[0].y); 37 | break; 38 | 39 | case NSLineToBezierPathElement: 40 | CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y); 41 | didClosePath = NO; 42 | break; 43 | 44 | case NSCurveToBezierPathElement: 45 | CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y, 46 | points[1].x, points[1].y, 47 | points[2].x, points[2].y); 48 | didClosePath = NO; 49 | break; 50 | 51 | case NSClosePathBezierPathElement: 52 | CGPathCloseSubpath(path); 53 | didClosePath = YES; 54 | break; 55 | } 56 | } 57 | 58 | // Be sure the path is closed or Quartz may not do valid hit detection. 59 | if (!didClosePath) 60 | CGPathCloseSubpath(path); 61 | 62 | immutablePath = CGPathCreateCopy(path); 63 | CGPathRelease(path); 64 | } 65 | 66 | return immutablePath; 67 | } 68 | 69 | @end 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /CFAAction/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /CFAActionTests/CFAActionSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFAActionSpec.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // Released under the MIT license. 8 | // 9 | 10 | SpecBegin(CFAAction) 11 | 12 | __block Class CFAActionClass = Nil; 13 | 14 | describe(@"Convenience Initializers", ^{ 15 | before(^{ 16 | CFAActionClass = CFAAction.class; 17 | }); 18 | 19 | it(@"should always dispense a subclass for move operations", ^{ 20 | CFAAction *action = nil; 21 | 22 | action = [CFAAction moveByX:0 y:0 duration:0]; 23 | expect(action.class).notTo.equal(CFAActionClass); 24 | 25 | #if CGVECTOR_DEFINED 26 | action = [CFAAction moveBy:(CGVector){ 0, 0 } duration:0]; 27 | expect(action.class).notTo.equal(CFAActionClass); 28 | #endif 29 | 30 | action = [CFAAction moveTo:(CGPoint){ 0, 0 } duration:0]; 31 | expect(action.class).notTo.equal(CFAActionClass); 32 | 33 | action = [CFAAction moveToX:0 duration:0]; 34 | expect(action.class).notTo.equal(CFAActionClass); 35 | 36 | action = [CFAAction moveToY:0 duration:0]; 37 | expect(action.class).notTo.equal(CFAActionClass); 38 | }); 39 | 40 | it(@"should always dispense a subclass for rotate operations", ^{ 41 | CFAAction *action = nil; 42 | 43 | action = [CFAAction rotateByAngle:0 duration: 0]; 44 | expect(action.class).notTo.equal(CFAActionClass); 45 | 46 | action = [CFAAction rotateToAngle:0 duration: 0]; 47 | expect(action.class).notTo.equal(CFAActionClass); 48 | 49 | action = [CFAAction rotateToAngle:0 duration: 0 shortestUnitArc:NO]; 50 | expect(action.class).notTo.equal(CFAActionClass); 51 | }); 52 | 53 | it(@"should always dispense a subclass for resize operations", ^{ 54 | CFAAction *action = nil; 55 | 56 | action = [CFAAction resizeByWidth:0 height:0 duration:0]; 57 | expect(action.class).notTo.equal(CFAActionClass); 58 | 59 | action = [CFAAction resizeToWidth:0 height:0 duration:0]; 60 | expect(action.class).notTo.equal(CFAActionClass); 61 | 62 | action = [CFAAction resizeToWidth:0 duration:0]; 63 | expect(action.class).notTo.equal(CFAActionClass); 64 | 65 | action = [CFAAction resizeToHeight:0 duration:0]; 66 | expect(action.class).notTo.equal(CFAActionClass); 67 | }); 68 | 69 | it(@"should always dispense a subclass for scale operations", ^{ 70 | CFAAction *action = nil; 71 | 72 | action = [CFAAction scaleBy:0 duration: 0]; 73 | expect(action.class).notTo.equal(CFAActionClass); 74 | 75 | action = [CFAAction scaleXBy:0 y:0 duration: 0]; 76 | expect(action.class).notTo.equal(CFAActionClass); 77 | 78 | action = [CFAAction scaleTo:0 duration: 0]; 79 | expect(action.class).notTo.equal(CFAActionClass); 80 | 81 | action = [CFAAction scaleXTo:0 y:0 duration: 0]; 82 | expect(action.class).notTo.equal(CFAActionClass); 83 | 84 | action = [CFAAction scaleXTo:0 duration: 0]; 85 | expect(action.class).notTo.equal(CFAActionClass); 86 | 87 | action = [CFAAction scaleYTo:0 duration: 0]; 88 | expect(action.class).notTo.equal(CFAActionClass); 89 | }); 90 | 91 | it(@"should always dispense a subclass for sequencing and grouping operations", ^{ 92 | CFAAction *action = nil; 93 | 94 | action = [CFAAction sequence:@[]]; 95 | expect(action.class).notTo.equal(CFAActionClass); 96 | 97 | action = [CFAAction group:@[]]; 98 | expect(action.class).notTo.equal(CFAActionClass); 99 | }); 100 | 101 | it(@"should always dispense a subclass for repeat operations", ^{ 102 | CFAAction *action = nil; 103 | 104 | action = [CFAAction repeatAction:[CFAAction scaleBy:0 duration: 0] count:0]; 105 | expect(action.class).notTo.equal(CFAActionClass); 106 | 107 | action = [CFAAction repeatActionForever:[CFAAction scaleBy:0 duration: 0]]; 108 | expect(action.class).notTo.equal(CFAActionClass); 109 | 110 | }); 111 | 112 | it(@"should always dispense a subclass for fade operations", ^{ 113 | CFAAction *action = nil; 114 | 115 | action = [CFAAction fadeInWithDuration:0]; 116 | expect(action.class).notTo.equal(CFAActionClass); 117 | 118 | action = [CFAAction fadeOutWithDuration:0]; 119 | expect(action.class).notTo.equal(CFAActionClass); 120 | 121 | action = [CFAAction fadeAlphaBy:0 duration:0]; 122 | expect(action.class).notTo.equal(CFAActionClass); 123 | 124 | action = [CFAAction fadeAlphaTo:0 duration:0]; 125 | expect(action.class).notTo.equal(CFAActionClass); 126 | }); 127 | 128 | it(@"should always dispense a subclass for path following operations", ^{ 129 | CFAAction *action = nil; 130 | CGMutablePathRef path = CGPathCreateMutable(); 131 | 132 | action = [CFAAction followPath:path duration:0]; 133 | expect(action.class).notTo.equal(CFAActionClass); 134 | 135 | action = [CFAAction followPath:path asOffset:NO orientToPath:NO duration:0]; 136 | expect(action.class).notTo.equal(CFAActionClass); 137 | 138 | CGPathRelease(path); 139 | }); 140 | 141 | it(@"should always dispense a subclass for fade operations", ^{ 142 | CFAAction *action = nil; 143 | 144 | action = [CFAAction speedBy:0 duration: 0]; 145 | expect(action.class).notTo.equal(CFAActionClass); 146 | 147 | action = [CFAAction speedTo:0 duration: 0]; 148 | expect(action.class).notTo.equal(CFAActionClass); 149 | }); 150 | 151 | it(@"should always dispense a subclass for wait operations", ^{ 152 | CFAAction *action = nil; 153 | 154 | action = [CFAAction waitForDuration:0]; 155 | expect(action.class).notTo.equal(CFAActionClass); 156 | 157 | action = [CFAAction waitForDuration:0 withRange:0]; 158 | expect(action.class).notTo.equal(CFAActionClass); 159 | }); 160 | 161 | it(@"should always dispense a subclass for action operations", ^{ 162 | CFAAction *action = nil; 163 | 164 | action = [CFAAction performSelector:@selector(description) onTarget:self]; 165 | expect(action.class).notTo.equal(CFAActionClass); 166 | 167 | action = [CFAAction runBlock:^{ }]; 168 | expect(action.class).notTo.equal(CFAActionClass); 169 | 170 | action = [CFAAction runBlock:^{ } queue:dispatch_get_main_queue()]; 171 | expect(action.class).notTo.equal(CFAActionClass); 172 | 173 | action = [CFAAction customActionWithDuration:0 actionBlock:^(CALayer * _Nonnull node, CGFloat elapsedTime) { }]; 174 | expect(action.class).notTo.equal(CFAActionClass); 175 | }); 176 | }); 177 | 178 | SpecEnd -------------------------------------------------------------------------------- /CFAActionTests/CFAActionTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /CFAActionTests/CFAActionTests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // CFAActionTests-Prefix.pch 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // 8 | 9 | #ifndef CFAAction_CFAActionTests_Prefix_pch 10 | #define CFAAction_CFAActionTests_Prefix_pch 11 | 12 | #import 13 | #import 14 | #import 15 | 16 | #define EXP_SHORTHAND 17 | #import "Specta.h" 18 | #import "Expecta.h" 19 | 20 | #import "CFATestLayer.h" 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /CFAActionTests/CFAPerformSelectorSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFAPerformSelector.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 4/23/14. 6 | // Copyright (c) 2014 CodaFi. All rights reserved. 7 | // 8 | 9 | SpecBegin(CFAPerformSelector) 10 | 11 | static CGRect const CFITestLayerRect = (CGRect){ 100, 100, 100, 100 }; 12 | __block CFATestLayer *layer = nil; 13 | 14 | describe(@"performSelector:onTarget:", ^{ 15 | beforeEach(^{ 16 | layer = CFATestLayer.layer; 17 | layer.frame = CFITestLayerRect; 18 | }); 19 | 20 | it(@"should recieve a selector", ^{ 21 | CFAAction *action = nil; 22 | 23 | expect(layer.gotIt).to.beFalsy(); 24 | action = [CFAAction performSelector:@selector(knockKnock) onTarget:layer]; 25 | [layer cfa_runAction:action completion:^{ 26 | expect(layer.gotIt).to.beTruthy(); 27 | }]; 28 | }); 29 | }); 30 | 31 | 32 | SpecEnd -------------------------------------------------------------------------------- /CFAActionTests/CFARunBlockSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFARunBlockSpec.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 4/23/14. 6 | // Copyright (c) 2014 CodaFi. All rights reserved. 7 | // 8 | 9 | SpecBegin(CFARunBlock) 10 | 11 | static CGRect const CFITestLayerRect = (CGRect){ 100, 100, 100, 100 }; 12 | __block CFATestLayer *layer = nil; 13 | 14 | describe(@"performSelector:onTarget:", ^{ 15 | beforeEach(^{ 16 | layer = CFATestLayer.layer; 17 | layer.frame = CFITestLayerRect; 18 | }); 19 | 20 | it(@"should recieve a selector", ^{ 21 | CFAAction *action = nil; 22 | 23 | expect(layer.gotIt).to.beFalsy(); 24 | action = [CFAAction runBlock:^{ 25 | [layer knockKnock]; 26 | }]; 27 | [layer cfa_runAction:action completion:^{ 28 | expect(layer.gotIt).will.beTruthy(); 29 | }]; 30 | }); 31 | }); 32 | 33 | 34 | SpecEnd -------------------------------------------------------------------------------- /CFAActionTests/CFATestLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFATestLayer.h 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 4/23/14. 6 | // Copyright (c) 2014 CodaFi. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CFATestLayer : CALayer 12 | 13 | - (void)knockKnock; 14 | 15 | @property (nonatomic) BOOL gotIt; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /CFAActionTests/CFATestLayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFATestLayer.m 3 | // CFAAction 4 | // 5 | // Created by Robert Widmann on 4/23/14. 6 | // Copyright (c) 2014 CodaFi. All rights reserved. 7 | // 8 | 9 | #import "CFATestLayer.h" 10 | 11 | @implementation CFATestLayer { 12 | CGRect _frame; 13 | } 14 | 15 | - (void)knockKnock { 16 | self.gotIt = YES; 17 | } 18 | 19 | - (void)setFrame:(CGRect)frame { 20 | _frame = frame; 21 | } 22 | 23 | - (CGRect)frame { 24 | return _frame; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /CFAActionTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/CAActionDemo/CAActionDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 84FE96E71810A5C5001BCD56 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FE96E61810A5C5001BCD56 /* Cocoa.framework */; }; 11 | 84FE96F11810A5C5001BCD56 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 84FE96EF1810A5C5001BCD56 /* InfoPlist.strings */; }; 12 | 84FE96F31810A5C5001BCD56 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 84FE96F21810A5C5001BCD56 /* main.m */; }; 13 | 84FE96F71810A5C5001BCD56 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 84FE96F51810A5C5001BCD56 /* Credits.rtf */; }; 14 | 84FE96FA1810A5C5001BCD56 /* CFIAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 84FE96F91810A5C5001BCD56 /* CFIAppDelegate.m */; }; 15 | 84FE96FD1810A5C5001BCD56 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 84FE96FB1810A5C5001BCD56 /* MainMenu.xib */; }; 16 | 84FE96FF1810A5C5001BCD56 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 84FE96FE1810A5C5001BCD56 /* Images.xcassets */; }; 17 | 84FE97061810A5C5001BCD56 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FE97051810A5C5001BCD56 /* XCTest.framework */; }; 18 | 84FE97071810A5C5001BCD56 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FE96E61810A5C5001BCD56 /* Cocoa.framework */; }; 19 | 84FE97251810A5F7001BCD56 /* CFAAction.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FE97221810A5CC001BCD56 /* CFAAction.framework */; }; 20 | 84FE97271810A688001BCD56 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FE97261810A688001BCD56 /* QuartzCore.framework */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | 84FE97081810A5C5001BCD56 /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = 84FE96DB1810A5C5001BCD56 /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = 84FE96E21810A5C5001BCD56; 29 | remoteInfo = CAActionDemo; 30 | }; 31 | 84FE97211810A5CC001BCD56 /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = 84FE971A1810A5CB001BCD56 /* CFAAction.xcodeproj */; 34 | proxyType = 2; 35 | remoteGlobalIDString = 843FD48E180F38B6002CDEA7; 36 | remoteInfo = CFAAction; 37 | }; 38 | 84FE97231810A5CC001BCD56 /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = 84FE971A1810A5CB001BCD56 /* CFAAction.xcodeproj */; 41 | proxyType = 2; 42 | remoteGlobalIDString = 843FD4A5180F38B7002CDEA7; 43 | remoteInfo = CFAActionTests; 44 | }; 45 | 84FE97281810A6BB001BCD56 /* PBXContainerItemProxy */ = { 46 | isa = PBXContainerItemProxy; 47 | containerPortal = 84FE971A1810A5CB001BCD56 /* CFAAction.xcodeproj */; 48 | proxyType = 1; 49 | remoteGlobalIDString = 843FD48D180F38B6002CDEA7; 50 | remoteInfo = CFAAction; 51 | }; 52 | /* End PBXContainerItemProxy section */ 53 | 54 | /* Begin PBXFileReference section */ 55 | 84FE96E31810A5C5001BCD56 /* CAActionDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CAActionDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | 84FE96E61810A5C5001BCD56 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 57 | 84FE96E91810A5C5001BCD56 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 58 | 84FE96EA1810A5C5001BCD56 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 59 | 84FE96EB1810A5C5001BCD56 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 60 | 84FE96EE1810A5C5001BCD56 /* CAActionDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CAActionDemo-Info.plist"; sourceTree = ""; }; 61 | 84FE96F01810A5C5001BCD56 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 62 | 84FE96F21810A5C5001BCD56 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 63 | 84FE96F41810A5C5001BCD56 /* CAActionDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CAActionDemo-Prefix.pch"; sourceTree = ""; }; 64 | 84FE96F61810A5C5001BCD56 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; 65 | 84FE96F81810A5C5001BCD56 /* CFIAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CFIAppDelegate.h; sourceTree = ""; }; 66 | 84FE96F91810A5C5001BCD56 /* CFIAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CFIAppDelegate.m; sourceTree = ""; }; 67 | 84FE96FC1810A5C5001BCD56 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 68 | 84FE96FE1810A5C5001BCD56 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 69 | 84FE97041810A5C5001BCD56 /* CAActionDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CAActionDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 70 | 84FE97051810A5C5001BCD56 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 71 | 84FE971A1810A5CB001BCD56 /* CFAAction.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CFAAction.xcodeproj; path = ../../CFAAction.xcodeproj; sourceTree = ""; }; 72 | 84FE97261810A688001BCD56 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 73 | /* End PBXFileReference section */ 74 | 75 | /* Begin PBXFrameworksBuildPhase section */ 76 | 84FE96E01810A5C5001BCD56 /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | 84FE97271810A688001BCD56 /* QuartzCore.framework in Frameworks */, 81 | 84FE97251810A5F7001BCD56 /* CFAAction.framework in Frameworks */, 82 | 84FE96E71810A5C5001BCD56 /* Cocoa.framework in Frameworks */, 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | 84FE97011810A5C5001BCD56 /* Frameworks */ = { 87 | isa = PBXFrameworksBuildPhase; 88 | buildActionMask = 2147483647; 89 | files = ( 90 | 84FE97071810A5C5001BCD56 /* Cocoa.framework in Frameworks */, 91 | 84FE97061810A5C5001BCD56 /* XCTest.framework in Frameworks */, 92 | ); 93 | runOnlyForDeploymentPostprocessing = 0; 94 | }; 95 | /* End PBXFrameworksBuildPhase section */ 96 | 97 | /* Begin PBXGroup section */ 98 | 84FE96DA1810A5C5001BCD56 = { 99 | isa = PBXGroup; 100 | children = ( 101 | 84FE971A1810A5CB001BCD56 /* CFAAction.xcodeproj */, 102 | 84FE96EC1810A5C5001BCD56 /* CAActionDemo */, 103 | 84FE96E51810A5C5001BCD56 /* Frameworks */, 104 | 84FE96E41810A5C5001BCD56 /* Products */, 105 | ); 106 | sourceTree = ""; 107 | }; 108 | 84FE96E41810A5C5001BCD56 /* Products */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 84FE96E31810A5C5001BCD56 /* CAActionDemo.app */, 112 | 84FE97041810A5C5001BCD56 /* CAActionDemoTests.xctest */, 113 | ); 114 | name = Products; 115 | sourceTree = ""; 116 | }; 117 | 84FE96E51810A5C5001BCD56 /* Frameworks */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 84FE97261810A688001BCD56 /* QuartzCore.framework */, 121 | 84FE96E61810A5C5001BCD56 /* Cocoa.framework */, 122 | 84FE97051810A5C5001BCD56 /* XCTest.framework */, 123 | 84FE96E81810A5C5001BCD56 /* Other Frameworks */, 124 | ); 125 | name = Frameworks; 126 | sourceTree = ""; 127 | }; 128 | 84FE96E81810A5C5001BCD56 /* Other Frameworks */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 84FE96E91810A5C5001BCD56 /* AppKit.framework */, 132 | 84FE96EA1810A5C5001BCD56 /* CoreData.framework */, 133 | 84FE96EB1810A5C5001BCD56 /* Foundation.framework */, 134 | ); 135 | name = "Other Frameworks"; 136 | sourceTree = ""; 137 | }; 138 | 84FE96EC1810A5C5001BCD56 /* CAActionDemo */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | 84FE96F81810A5C5001BCD56 /* CFIAppDelegate.h */, 142 | 84FE96F91810A5C5001BCD56 /* CFIAppDelegate.m */, 143 | 84FE96FB1810A5C5001BCD56 /* MainMenu.xib */, 144 | 84FE96FE1810A5C5001BCD56 /* Images.xcassets */, 145 | 84FE96ED1810A5C5001BCD56 /* Supporting Files */, 146 | ); 147 | path = CAActionDemo; 148 | sourceTree = ""; 149 | }; 150 | 84FE96ED1810A5C5001BCD56 /* Supporting Files */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | 84FE96EE1810A5C5001BCD56 /* CAActionDemo-Info.plist */, 154 | 84FE96EF1810A5C5001BCD56 /* InfoPlist.strings */, 155 | 84FE96F21810A5C5001BCD56 /* main.m */, 156 | 84FE96F41810A5C5001BCD56 /* CAActionDemo-Prefix.pch */, 157 | 84FE96F51810A5C5001BCD56 /* Credits.rtf */, 158 | ); 159 | name = "Supporting Files"; 160 | sourceTree = ""; 161 | }; 162 | 84FE971B1810A5CB001BCD56 /* Products */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | 84FE97221810A5CC001BCD56 /* CFAAction.framework */, 166 | 84FE97241810A5CC001BCD56 /* CFAActionTests.octest */, 167 | ); 168 | name = Products; 169 | sourceTree = ""; 170 | }; 171 | /* End PBXGroup section */ 172 | 173 | /* Begin PBXNativeTarget section */ 174 | 84FE96E21810A5C5001BCD56 /* CAActionDemo */ = { 175 | isa = PBXNativeTarget; 176 | buildConfigurationList = 84FE97141810A5C5001BCD56 /* Build configuration list for PBXNativeTarget "CAActionDemo" */; 177 | buildPhases = ( 178 | 84FE96DF1810A5C5001BCD56 /* Sources */, 179 | 84FE96E01810A5C5001BCD56 /* Frameworks */, 180 | 84FE96E11810A5C5001BCD56 /* Resources */, 181 | ); 182 | buildRules = ( 183 | ); 184 | dependencies = ( 185 | 84FE97291810A6BB001BCD56 /* PBXTargetDependency */, 186 | ); 187 | name = CAActionDemo; 188 | productName = CAActionDemo; 189 | productReference = 84FE96E31810A5C5001BCD56 /* CAActionDemo.app */; 190 | productType = "com.apple.product-type.application"; 191 | }; 192 | 84FE97031810A5C5001BCD56 /* CAActionDemoTests */ = { 193 | isa = PBXNativeTarget; 194 | buildConfigurationList = 84FE97171810A5C5001BCD56 /* Build configuration list for PBXNativeTarget "CAActionDemoTests" */; 195 | buildPhases = ( 196 | 84FE97001810A5C5001BCD56 /* Sources */, 197 | 84FE97011810A5C5001BCD56 /* Frameworks */, 198 | 84FE97021810A5C5001BCD56 /* Resources */, 199 | ); 200 | buildRules = ( 201 | ); 202 | dependencies = ( 203 | 84FE97091810A5C5001BCD56 /* PBXTargetDependency */, 204 | ); 205 | name = CAActionDemoTests; 206 | productName = CAActionDemoTests; 207 | productReference = 84FE97041810A5C5001BCD56 /* CAActionDemoTests.xctest */; 208 | productType = "com.apple.product-type.bundle.unit-test"; 209 | }; 210 | /* End PBXNativeTarget section */ 211 | 212 | /* Begin PBXProject section */ 213 | 84FE96DB1810A5C5001BCD56 /* Project object */ = { 214 | isa = PBXProject; 215 | attributes = { 216 | CLASSPREFIX = CFI; 217 | LastUpgradeCheck = 0500; 218 | ORGANIZATIONNAME = CodaFi; 219 | TargetAttributes = { 220 | 84FE97031810A5C5001BCD56 = { 221 | TestTargetID = 84FE96E21810A5C5001BCD56; 222 | }; 223 | }; 224 | }; 225 | buildConfigurationList = 84FE96DE1810A5C5001BCD56 /* Build configuration list for PBXProject "CAActionDemo" */; 226 | compatibilityVersion = "Xcode 3.2"; 227 | developmentRegion = English; 228 | hasScannedForEncodings = 0; 229 | knownRegions = ( 230 | en, 231 | Base, 232 | ); 233 | mainGroup = 84FE96DA1810A5C5001BCD56; 234 | productRefGroup = 84FE96E41810A5C5001BCD56 /* Products */; 235 | projectDirPath = ""; 236 | projectReferences = ( 237 | { 238 | ProductGroup = 84FE971B1810A5CB001BCD56 /* Products */; 239 | ProjectRef = 84FE971A1810A5CB001BCD56 /* CFAAction.xcodeproj */; 240 | }, 241 | ); 242 | projectRoot = ""; 243 | targets = ( 244 | 84FE96E21810A5C5001BCD56 /* CAActionDemo */, 245 | 84FE97031810A5C5001BCD56 /* CAActionDemoTests */, 246 | ); 247 | }; 248 | /* End PBXProject section */ 249 | 250 | /* Begin PBXReferenceProxy section */ 251 | 84FE97221810A5CC001BCD56 /* CFAAction.framework */ = { 252 | isa = PBXReferenceProxy; 253 | fileType = wrapper.framework; 254 | path = CFAAction.framework; 255 | remoteRef = 84FE97211810A5CC001BCD56 /* PBXContainerItemProxy */; 256 | sourceTree = BUILT_PRODUCTS_DIR; 257 | }; 258 | 84FE97241810A5CC001BCD56 /* CFAActionTests.octest */ = { 259 | isa = PBXReferenceProxy; 260 | fileType = wrapper.cfbundle; 261 | path = CFAActionTests.octest; 262 | remoteRef = 84FE97231810A5CC001BCD56 /* PBXContainerItemProxy */; 263 | sourceTree = BUILT_PRODUCTS_DIR; 264 | }; 265 | /* End PBXReferenceProxy section */ 266 | 267 | /* Begin PBXResourcesBuildPhase section */ 268 | 84FE96E11810A5C5001BCD56 /* Resources */ = { 269 | isa = PBXResourcesBuildPhase; 270 | buildActionMask = 2147483647; 271 | files = ( 272 | 84FE96F11810A5C5001BCD56 /* InfoPlist.strings in Resources */, 273 | 84FE96FF1810A5C5001BCD56 /* Images.xcassets in Resources */, 274 | 84FE96F71810A5C5001BCD56 /* Credits.rtf in Resources */, 275 | 84FE96FD1810A5C5001BCD56 /* MainMenu.xib in Resources */, 276 | ); 277 | runOnlyForDeploymentPostprocessing = 0; 278 | }; 279 | 84FE97021810A5C5001BCD56 /* Resources */ = { 280 | isa = PBXResourcesBuildPhase; 281 | buildActionMask = 2147483647; 282 | files = ( 283 | ); 284 | runOnlyForDeploymentPostprocessing = 0; 285 | }; 286 | /* End PBXResourcesBuildPhase section */ 287 | 288 | /* Begin PBXSourcesBuildPhase section */ 289 | 84FE96DF1810A5C5001BCD56 /* Sources */ = { 290 | isa = PBXSourcesBuildPhase; 291 | buildActionMask = 2147483647; 292 | files = ( 293 | 84FE96F31810A5C5001BCD56 /* main.m in Sources */, 294 | 84FE96FA1810A5C5001BCD56 /* CFIAppDelegate.m in Sources */, 295 | ); 296 | runOnlyForDeploymentPostprocessing = 0; 297 | }; 298 | 84FE97001810A5C5001BCD56 /* Sources */ = { 299 | isa = PBXSourcesBuildPhase; 300 | buildActionMask = 2147483647; 301 | files = ( 302 | ); 303 | runOnlyForDeploymentPostprocessing = 0; 304 | }; 305 | /* End PBXSourcesBuildPhase section */ 306 | 307 | /* Begin PBXTargetDependency section */ 308 | 84FE97091810A5C5001BCD56 /* PBXTargetDependency */ = { 309 | isa = PBXTargetDependency; 310 | target = 84FE96E21810A5C5001BCD56 /* CAActionDemo */; 311 | targetProxy = 84FE97081810A5C5001BCD56 /* PBXContainerItemProxy */; 312 | }; 313 | 84FE97291810A6BB001BCD56 /* PBXTargetDependency */ = { 314 | isa = PBXTargetDependency; 315 | name = CFAAction; 316 | targetProxy = 84FE97281810A6BB001BCD56 /* PBXContainerItemProxy */; 317 | }; 318 | /* End PBXTargetDependency section */ 319 | 320 | /* Begin PBXVariantGroup section */ 321 | 84FE96EF1810A5C5001BCD56 /* InfoPlist.strings */ = { 322 | isa = PBXVariantGroup; 323 | children = ( 324 | 84FE96F01810A5C5001BCD56 /* en */, 325 | ); 326 | name = InfoPlist.strings; 327 | sourceTree = ""; 328 | }; 329 | 84FE96F51810A5C5001BCD56 /* Credits.rtf */ = { 330 | isa = PBXVariantGroup; 331 | children = ( 332 | 84FE96F61810A5C5001BCD56 /* en */, 333 | ); 334 | name = Credits.rtf; 335 | sourceTree = ""; 336 | }; 337 | 84FE96FB1810A5C5001BCD56 /* MainMenu.xib */ = { 338 | isa = PBXVariantGroup; 339 | children = ( 340 | 84FE96FC1810A5C5001BCD56 /* Base */, 341 | ); 342 | name = MainMenu.xib; 343 | sourceTree = ""; 344 | }; 345 | /* End PBXVariantGroup section */ 346 | 347 | /* Begin XCBuildConfiguration section */ 348 | 84FE97121810A5C5001BCD56 /* Debug */ = { 349 | isa = XCBuildConfiguration; 350 | buildSettings = { 351 | ALWAYS_SEARCH_USER_PATHS = NO; 352 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 353 | CLANG_CXX_LIBRARY = "libc++"; 354 | CLANG_ENABLE_OBJC_ARC = YES; 355 | CLANG_WARN_BOOL_CONVERSION = YES; 356 | CLANG_WARN_CONSTANT_CONVERSION = YES; 357 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 358 | CLANG_WARN_EMPTY_BODY = YES; 359 | CLANG_WARN_ENUM_CONVERSION = YES; 360 | CLANG_WARN_INT_CONVERSION = YES; 361 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 362 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 363 | COPY_PHASE_STRIP = NO; 364 | GCC_C_LANGUAGE_STANDARD = gnu99; 365 | GCC_DYNAMIC_NO_PIC = NO; 366 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 367 | GCC_OPTIMIZATION_LEVEL = 0; 368 | GCC_PREPROCESSOR_DEFINITIONS = ( 369 | "DEBUG=1", 370 | "$(inherited)", 371 | ); 372 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 373 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 374 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 375 | GCC_WARN_UNDECLARED_SELECTOR = YES; 376 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 377 | GCC_WARN_UNUSED_FUNCTION = YES; 378 | GCC_WARN_UNUSED_VARIABLE = YES; 379 | MACOSX_DEPLOYMENT_TARGET = 10.9; 380 | ONLY_ACTIVE_ARCH = YES; 381 | SDKROOT = macosx; 382 | }; 383 | name = Debug; 384 | }; 385 | 84FE97131810A5C5001BCD56 /* Release */ = { 386 | isa = XCBuildConfiguration; 387 | buildSettings = { 388 | ALWAYS_SEARCH_USER_PATHS = NO; 389 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 390 | CLANG_CXX_LIBRARY = "libc++"; 391 | CLANG_ENABLE_OBJC_ARC = YES; 392 | CLANG_WARN_BOOL_CONVERSION = YES; 393 | CLANG_WARN_CONSTANT_CONVERSION = YES; 394 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 395 | CLANG_WARN_EMPTY_BODY = YES; 396 | CLANG_WARN_ENUM_CONVERSION = YES; 397 | CLANG_WARN_INT_CONVERSION = YES; 398 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 399 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 400 | COPY_PHASE_STRIP = YES; 401 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 402 | ENABLE_NS_ASSERTIONS = NO; 403 | GCC_C_LANGUAGE_STANDARD = gnu99; 404 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 405 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 406 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 407 | GCC_WARN_UNDECLARED_SELECTOR = YES; 408 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 409 | GCC_WARN_UNUSED_FUNCTION = YES; 410 | GCC_WARN_UNUSED_VARIABLE = YES; 411 | MACOSX_DEPLOYMENT_TARGET = 10.9; 412 | SDKROOT = macosx; 413 | }; 414 | name = Release; 415 | }; 416 | 84FE97151810A5C5001BCD56 /* Debug */ = { 417 | isa = XCBuildConfiguration; 418 | buildSettings = { 419 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 420 | COMBINE_HIDPI_IMAGES = YES; 421 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 422 | GCC_PREFIX_HEADER = "CAActionDemo/CAActionDemo-Prefix.pch"; 423 | INFOPLIST_FILE = "CAActionDemo/CAActionDemo-Info.plist"; 424 | MACOSX_DEPLOYMENT_TARGET = 10.8; 425 | PRODUCT_NAME = "$(TARGET_NAME)"; 426 | WRAPPER_EXTENSION = app; 427 | }; 428 | name = Debug; 429 | }; 430 | 84FE97161810A5C5001BCD56 /* Release */ = { 431 | isa = XCBuildConfiguration; 432 | buildSettings = { 433 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 434 | COMBINE_HIDPI_IMAGES = YES; 435 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 436 | GCC_PREFIX_HEADER = "CAActionDemo/CAActionDemo-Prefix.pch"; 437 | INFOPLIST_FILE = "CAActionDemo/CAActionDemo-Info.plist"; 438 | MACOSX_DEPLOYMENT_TARGET = 10.8; 439 | PRODUCT_NAME = "$(TARGET_NAME)"; 440 | WRAPPER_EXTENSION = app; 441 | }; 442 | name = Release; 443 | }; 444 | 84FE97181810A5C5001BCD56 /* Debug */ = { 445 | isa = XCBuildConfiguration; 446 | buildSettings = { 447 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/CAActionDemo.app/Contents/MacOS/CAActionDemo"; 448 | COMBINE_HIDPI_IMAGES = YES; 449 | FRAMEWORK_SEARCH_PATHS = ( 450 | "$(DEVELOPER_FRAMEWORKS_DIR)", 451 | "$(inherited)", 452 | ); 453 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 454 | GCC_PREFIX_HEADER = "CAActionDemo/CAActionDemo-Prefix.pch"; 455 | GCC_PREPROCESSOR_DEFINITIONS = ( 456 | "DEBUG=1", 457 | "$(inherited)", 458 | ); 459 | INFOPLIST_FILE = "CAActionDemoTests/CAActionDemoTests-Info.plist"; 460 | MACOSX_DEPLOYMENT_TARGET = 10.8; 461 | PRODUCT_NAME = "$(TARGET_NAME)"; 462 | TEST_HOST = "$(BUNDLE_LOADER)"; 463 | WRAPPER_EXTENSION = xctest; 464 | }; 465 | name = Debug; 466 | }; 467 | 84FE97191810A5C5001BCD56 /* Release */ = { 468 | isa = XCBuildConfiguration; 469 | buildSettings = { 470 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/CAActionDemo.app/Contents/MacOS/CAActionDemo"; 471 | COMBINE_HIDPI_IMAGES = YES; 472 | FRAMEWORK_SEARCH_PATHS = ( 473 | "$(DEVELOPER_FRAMEWORKS_DIR)", 474 | "$(inherited)", 475 | ); 476 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 477 | GCC_PREFIX_HEADER = "CAActionDemo/CAActionDemo-Prefix.pch"; 478 | INFOPLIST_FILE = "CAActionDemoTests/CAActionDemoTests-Info.plist"; 479 | MACOSX_DEPLOYMENT_TARGET = 10.8; 480 | PRODUCT_NAME = "$(TARGET_NAME)"; 481 | TEST_HOST = "$(BUNDLE_LOADER)"; 482 | WRAPPER_EXTENSION = xctest; 483 | }; 484 | name = Release; 485 | }; 486 | /* End XCBuildConfiguration section */ 487 | 488 | /* Begin XCConfigurationList section */ 489 | 84FE96DE1810A5C5001BCD56 /* Build configuration list for PBXProject "CAActionDemo" */ = { 490 | isa = XCConfigurationList; 491 | buildConfigurations = ( 492 | 84FE97121810A5C5001BCD56 /* Debug */, 493 | 84FE97131810A5C5001BCD56 /* Release */, 494 | ); 495 | defaultConfigurationIsVisible = 0; 496 | defaultConfigurationName = Release; 497 | }; 498 | 84FE97141810A5C5001BCD56 /* Build configuration list for PBXNativeTarget "CAActionDemo" */ = { 499 | isa = XCConfigurationList; 500 | buildConfigurations = ( 501 | 84FE97151810A5C5001BCD56 /* Debug */, 502 | 84FE97161810A5C5001BCD56 /* Release */, 503 | ); 504 | defaultConfigurationIsVisible = 0; 505 | defaultConfigurationName = Release; 506 | }; 507 | 84FE97171810A5C5001BCD56 /* Build configuration list for PBXNativeTarget "CAActionDemoTests" */ = { 508 | isa = XCConfigurationList; 509 | buildConfigurations = ( 510 | 84FE97181810A5C5001BCD56 /* Debug */, 511 | 84FE97191810A5C5001BCD56 /* Release */, 512 | ); 513 | defaultConfigurationIsVisible = 0; 514 | defaultConfigurationName = Release; 515 | }; 516 | /* End XCConfigurationList section */ 517 | }; 518 | rootObject = 84FE96DB1810A5C5001BCD56 /* Project object */; 519 | } 520 | -------------------------------------------------------------------------------- /Example/CAActionDemo/CAActionDemo/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | Default 523 | 524 | 525 | 526 | 527 | 528 | 529 | Left to Right 530 | 531 | 532 | 533 | 534 | 535 | 536 | Right to Left 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | Default 548 | 549 | 550 | 551 | 552 | 553 | 554 | Left to Right 555 | 556 | 557 | 558 | 559 | 560 | 561 | Right to Left 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | -------------------------------------------------------------------------------- /Example/CAActionDemo/CAActionDemo/CAActionDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.codafi.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | NSHumanReadableCopyright 28 | Copyright © 2013 CodaFi. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /Example/CAActionDemo/CAActionDemo/CAActionDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #import 10 | #endif 11 | -------------------------------------------------------------------------------- /Example/CAActionDemo/CAActionDemo/CFIAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFIAppDelegate.h 3 | // CAActionDemo 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CFIAppDelegate : NSObject 12 | 13 | @property (assign) IBOutlet NSWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/CAActionDemo/CAActionDemo/CFIAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFIAppDelegate.m 3 | // CAActionDemo 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // 8 | 9 | #import "CFIAppDelegate.h" 10 | #import 11 | 12 | @interface CFIAppDelegate () 13 | 14 | @property (nonatomic, strong) CALayer *greenLayer; 15 | @property (nonatomic, strong) CALayer *redLayer; 16 | @property (nonatomic, strong) CALayer *yellowLayer; 17 | 18 | @end 19 | 20 | @implementation CFIAppDelegate 21 | 22 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 23 | _greenLayer = CALayer.layer; 24 | _greenLayer.frame = CGRectMake(-200, NSMidY([self.window.contentView frame]) - 50, 100, 100); 25 | _greenLayer.backgroundColor = [NSColor.greenColor CGColor]; 26 | _greenLayer.anchorPoint = CGPointMake(0.5, 0.5); 27 | 28 | _redLayer = CALayer.layer; 29 | _redLayer.frame = CGRectMake(NSMidX([self.window.contentView frame]) - 50, NSMidY([self.window.contentView frame]) - 50, 100, 100); 30 | _redLayer.backgroundColor = [NSColor.redColor CGColor]; 31 | _redLayer.anchorPoint = CGPointMake(0.5, 0.5); 32 | 33 | _yellowLayer = CALayer.layer; 34 | _yellowLayer.frame = CGRectMake(850, NSMidY([self.window.contentView frame]) - 50, 100, 100); 35 | _yellowLayer.backgroundColor = [NSColor.yellowColor CGColor]; 36 | _yellowLayer.anchorPoint = CGPointMake(0.5, 0.5); 37 | 38 | [[self.window.contentView layer] addSublayer:_greenLayer]; 39 | [[self.window.contentView layer] addSublayer:_redLayer]; 40 | [[self.window.contentView layer] addSublayer:_yellowLayer]; 41 | 42 | CFAAction *moveAction = [CFAAction moveByX:250 y:0 duration:1]; 43 | CFAAction *rotateAction = [CFAAction rotateToAngle:M_PI duration:1]; 44 | CFAAction *scaleAnimation = [CFAAction scaleBy:-0.25 duration:0.25]; 45 | CFAAction *growAnimation = [CFAAction resizeToHeight:400 duration:0.25]; 46 | 47 | [self.redLayer runAction:[CFAAction repeatAction:rotateAction count:7] completion:^{ 48 | [self.redLayer runAction:growAnimation]; 49 | }]; 50 | 51 | [self.greenLayer runAction:[CFAAction waitForDuration:0.5] completion:^{ 52 | [self.greenLayer runAction:[CFAAction sequence:@[ moveAction, [CFAAction repeatAction:rotateAction count:6], growAnimation ]] completion:^{ 53 | [self.greenLayer runAction:scaleAnimation]; 54 | }]; 55 | }]; 56 | 57 | [self.yellowLayer runAction:[CFAAction waitForDuration:0.5] completion:^{ 58 | [self.yellowLayer runAction:[CFAAction sequence:@[ [moveAction reversedAction], [CFAAction repeatAction:rotateAction count:6], growAnimation ]] completion:^{ 59 | [self.yellowLayer runAction:scaleAnimation]; 60 | }]; 61 | }]; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /Example/CAActionDemo/CAActionDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Example/CAActionDemo/CAActionDemo/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} 2 | {\colortbl;\red255\green255\blue255;} 3 | \paperw9840\paperh8400 4 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural 5 | 6 | \f0\b\fs24 \cf0 Engineering: 7 | \b0 \ 8 | Some people\ 9 | \ 10 | 11 | \b Human Interface Design: 12 | \b0 \ 13 | Some other people\ 14 | \ 15 | 16 | \b Testing: 17 | \b0 \ 18 | Hopefully not nobody\ 19 | \ 20 | 21 | \b Documentation: 22 | \b0 \ 23 | Whoever\ 24 | \ 25 | 26 | \b With special thanks to: 27 | \b0 \ 28 | Mom\ 29 | } 30 | -------------------------------------------------------------------------------- /Example/CAActionDemo/CAActionDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/CAActionDemo/CAActionDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CAActionDemo 4 | // 5 | // Created by Robert Widmann on 10/17/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, const char * argv[]) 12 | { 13 | return NSApplicationMain(argc, argv); 14 | } 15 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Robert Widmann 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## CFAAction ## 2 | 3 | [![Build Status](https://travis-ci.org/CodaFi/CFAAction.svg?branch=master)](https://travis-ci.org/CodaFi/CFAAction) 4 | 5 | `CFAAction` is a wrapper around several common Core Animation animations that provides additional modularity and the ability to group and sequence actions. 6 | 7 | ## Getting Started 8 | `CFAAction`'s should be treated like `SKAction`'s, which means they are only run when they are submitted to a variant of `-[CALayer runAction:]`. 9 | 10 | To run an action that moves a layer 10 pixels to the right in 1 second, for example, you can do: 11 | 12 | ```Objective-C 13 | CFAAction *moveAction = [CFAAction moveByX:10 y:0 duration:1]; 14 | [self.view.layer runAction:growAnimation]; 15 | ``` 16 | Completion blocks can be used to sequence animations: 17 | 18 | ```Objective-C 19 | CFAAction *rotateAction = [CFAAction rotateToAngle:M_PI duration:1]; 20 | CFAAction *growAnimation = [CFAAction resizeToHeight:400 duration:0.25]; 21 | 22 | [self.view.layer runAction:rotateAction completion:^{ 23 | [self.view.layer runAction:growAnimation]; 24 | }]; 25 | ``` 26 | 27 | But, so can `+[CFAAction sequence:]`: 28 | 29 | ```Objective-C 30 | CFAAction *rotateAction = [CFAAction rotateToAngle:M_PI duration:1]; 31 | CFAAction *growAnimation = [CFAAction resizeToHeight:400 duration:0.25]; 32 | 33 | [self.view.layer runAction:[CFAAction sequence:@[ rotateAction, growAnimation ]]; 34 | ``` 35 | 36 | To run actions concurrently, there's `+[CFAAction group:]`: 37 | 38 | ```Objective-C 39 | CFAAction *rotateAction = [CFAAction rotateToAngle:M_PI duration:1]; 40 | CFAAction *scaleAnimation = [CFAAction scaleBy:-0.25 duration:0.25]; 41 | [self.view.layer runAction:[CFAAction group:@[ scaleAnimation, rotateAction ]]]; 42 | ``` 43 | 44 | To perform a block or selector as an action, use `[CFAAnimation runBlock:]`, `[CFAAnimation runBlock:queue:]`, or `[CFAAnimation performSelector:onTarget:]`. When used in a group with a wait action, these can act as extensions for the total duration of the grouping. When used in a sequence, they can act as delays before or after the selector is performed for precision timing. 45 | 46 | ```Objective-C 47 | [self.planeLayer runAction:[CFAAction sequence:@[ bankAndRollAction, [CFAAction repeatAction:fireAction count:6] ]] completion:^{ 48 | [self.enemy runAction:[CFAAction sequence:@[ dieAction, [CFAAction performSelector:@selector(die:) onTarget:self.enemy], [CFAAction waitForDuration:0.5] ]]]; 49 | }]; 50 | ``` 51 | ## License ## 52 | `CFAAction` is licensed under the [MIT](http://opensource.org/licenses/MIT) license. See [LICENSE.md](LICENSE.md). 53 | 54 | ## Contact ## 55 | Follow me on Twitter [@CodaFi_](https://twitter.com/CodaFi_), or visit [my website](http://λπω.com/). --------------------------------------------------------------------------------