├── .gitignore ├── AppledocSettings.plist ├── CHANGELOG.md ├── Classes ├── EasingTypes │ ├── PMTweenEasing.h │ ├── PMTweenEasingBack.h │ ├── PMTweenEasingBack.m │ ├── PMTweenEasingBounce.h │ ├── PMTweenEasingBounce.m │ ├── PMTweenEasingCircular.h │ ├── PMTweenEasingCircular.m │ ├── PMTweenEasingCubic.h │ ├── PMTweenEasingCubic.m │ ├── PMTweenEasingElastic.h │ ├── PMTweenEasingElastic.m │ ├── PMTweenEasingExpo.h │ ├── PMTweenEasingExpo.m │ ├── PMTweenEasingLinear.h │ ├── PMTweenEasingLinear.m │ ├── PMTweenEasingQuadratic.h │ ├── PMTweenEasingQuadratic.m │ ├── PMTweenEasingQuartic.h │ ├── PMTweenEasingQuartic.m │ ├── PMTweenEasingQuintic.h │ ├── PMTweenEasingQuintic.m │ ├── PMTweenEasingSine.h │ ├── PMTweenEasingSine.m │ └── PMTweenEasingTypes.h ├── PMTween.h ├── PMTweenBeat.h ├── PMTweenBeat.m ├── PMTweenCATempo.h ├── PMTweenCATempo.m ├── PMTweenGroup.h ├── PMTweenGroup.m ├── PMTweenObjectUpdater.h ├── PMTweenObjectUpdater.m ├── PMTweenPhysicsSystem.h ├── PMTweenPhysicsSystem.m ├── PMTweenPhysicsUnit.h ├── PMTweenPhysicsUnit.m ├── PMTweenSequence.h ├── PMTweenSequence.m ├── PMTweenSupport.h ├── PMTweenSupport.m ├── PMTweenTempo.h ├── PMTweenTempo.m ├── PMTweenUnit.h ├── PMTweenUnit.m ├── PMTweening.h └── PMTweening.m ├── Examples ├── PMTweenExamples.xcodeproj │ └── project.pbxproj └── PMTweenExamples │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Classes │ ├── AdditiveVC.h │ ├── AdditiveVC.m │ ├── BasicPhysicsTweenVC.h │ ├── BasicPhysicsTweenVC.m │ ├── BasicTweenVC.h │ ├── BasicTweenVC.m │ ├── DynamicTweenVC.h │ ├── DynamicTweenVC.m │ ├── GroupTweenVC.h │ ├── GroupTweenVC.m │ ├── MassTweensVC.h │ ├── MassTweensVC.m │ ├── SequenceNoncontiguousVC.h │ ├── SequenceNoncontiguousVC.m │ ├── SequenceTweenVC.h │ ├── SequenceTweenVC.m │ ├── TableViewController.h │ ├── TableViewController.m │ ├── Transform3DVC.h │ └── Transform3DVC.m │ ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json │ ├── Launch Screen.storyboard │ ├── PMTweenExamples-Info.plist │ ├── PMTweenExamples-Prefix.pch │ ├── en.lproj │ └── InfoPlist.strings │ └── main.m ├── LICENSE.md ├── PMTween.podspec ├── README.md └── Tests ├── .gitignore ├── PMTweenTests.xcodeproj └── project.pbxproj ├── PMTweenTests ├── Classes │ ├── TestObject.h │ └── TestObject.m ├── PMTweenTests-Info.plist ├── PMTweenTests-Prefix.pch ├── Specs │ ├── PMTweenBeatSpec.m │ ├── PMTweenEasingSpec.m │ ├── PMTweenGroupSpec.m │ ├── PMTweenObjectUpdaterSpec.m │ ├── PMTweenPhysicsUnitSpec.m │ ├── PMTweenSequenceSpec.m │ └── PMTweenUnitSpec.m └── en.lproj │ └── InfoPlist.strings └── Podfile /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X Finder and whatnot 2 | .DS_Store 3 | 4 | # rvm files 5 | .ruby-version 6 | .ruby-gemset 7 | Gemfile 8 | Gemfile.lock 9 | 10 | # Xcode 11 | build/ 12 | *.pbxuser 13 | !default.pbxuser 14 | *.mode1v3 15 | !default.mode1v3 16 | *.mode2v3 17 | !default.mode2v3 18 | *.perspectivev3 19 | !default.perspectivev3 20 | *.xcworkspace 21 | !default.xcworkspace 22 | xcuserdata 23 | profile 24 | *.moved-aside 25 | DerivedData 26 | .idea/ 27 | 28 | docs_build/ 29 | Documentation/ 30 | development/ -------------------------------------------------------------------------------- /AppledocSettings.plist: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | --input 7 | 8 | Classes/ 9 | 10 | --output 11 | 12 | docs_build/ 13 | 14 | --project-name 15 | PMTween 16 | --project-company 17 | Poet & Mountain, LLC 18 | --company-id 19 | com.poetmountain 20 | --create-docset 21 | 22 | --install-docset 23 | 24 | --repeat-first-par 25 | 26 | --verbose 27 | 4 28 | --logformat 29 | 1 30 | --docset-platform-family 31 | iphoneos 32 | 33 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 1.3.7 2 | ===== 3 | * PMTweenCATempo now uses NSRunLoopCommonModes 4 | 5 | 1.3.6 6 | ===== 7 | * Fixed bug with Bounce easingIn type 8 | * PMTweenPhysicsUnit now uses a higher-resolution timestamp 9 | 10 | 1.3.5 11 | ===== 12 | * Fixed bug in the Back easingOut easing equation 13 | * Moved the call of PMTweenUnit's updateBlock to after the property update 14 | 15 | 1.3.4 16 | ===== 17 | * PMTween header import now properly includes the constants from PMTweening 18 | * Improved dispatch timer dealloc checks in PMTweenPhysicsUnit 19 | 20 | 1.3.3 21 | ===== 22 | * PMTweenPhysicsSystem (used internally by PMTweenPhysicsUnit) now uses a fixed timestep, which though rudimentary, helps smooth out tween value jittering 23 | * Fixed bug with physics timer not properly removed 24 | 25 | 1.3.2 26 | ===== 27 | * PMTweenPhysicsUnit now updates its physics system independently of property updates, at a frequency of 120 fps by default (to provide double the resolution of a normal 60fps app). This can be changed by PMTweenPhysicsUnit's new property physicsTimerInterval. 28 | * PMTweenPhysicsSystem now handles friction values with less accumulated error. 29 | * PMTweenPhysicsUnit's velocity is now expressed in units per second. Prior to this the velocity acted as units per frame, which obviously caused inconsistent simulations. 30 | * Minor updates to the Examples project. 31 | 32 | 1.3.1 33 | ===== 34 | * Updated project architecture to use single header import 35 | * Fixed string equality check in PMTweenSupport when comparing keyPaths 36 | * Miscellaneous project cleanup 37 | 38 | 1.3.0 39 | ===== 40 | * Added support for CGVector structs as property tweening targets. 41 | * Added support for tvOS as a target. 42 | * Updated init methods to use instancetype. 43 | * Changed minimum iOS target to 7.0. 44 | 45 | 1.2.2 46 | ===== 47 | * Fixed additional warnings 48 | * Modified one test 49 | * updated Podspec 50 | 51 | 1.2.1 52 | ===== 53 | * Updated Podspec 54 | 55 | 1.2.0 56 | ===== 57 | * Implemented additive animation in PMTweenUnit! 58 | * Updated PMTweenObjectUpdater to provide additive updating support. 59 | * Updated tests. 60 | * Updated Examples project to include additive demos. 61 | 62 | 1.1.2 63 | ===== 64 | * Fixed multiple tweens targeting same object property incorrectly overriding initial value of first tween. 65 | 66 | 1.1.1 67 | ===== 68 | * Fixed PMTweenUnit and PMTweenPhysicsUnit failing to tween object properties specified by a single-element keyPath. 69 | * Fixed NSNumber object properties with a nil value not tweening. Such properties are now initialized with the specified starting value during the init method. 70 | * Changed all init methods to return instancetype instead of id. 71 | * More tests. 72 | 73 | 1.1.0 74 | ===== 75 | * Added PMTweenPhysicsUnit and PMTweenPhysicsSystem classes to provide dynamic, physics-based tweening! PMTweenPhysicsUnit adopts PMTweening, so you get all the standard PMTween functionality – use it in groups and sequences, add reversing, pause it, etc. See the documentation and examples project for more info. 76 | * Fixed bug where PMTweening classes set to reversing wouldn't reverse properly inside a PMTweenGroup that wasn't itself reversing. 77 | currentValue property wasn't being set to startingValue in PMTweenUnit on setup. 78 | * New tests. 79 | * Updated examples project. 80 | 81 | 1.0.1 82 | ===== 83 | * PMTweenBeat: Fixed this class not having a default tempo or setting child tween tempos to nil. 84 | * PMTweenGroup: Base init method was not calling setTempo. 85 | 86 | 1.0.0 87 | ===== 88 | * First release 89 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasing.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasing.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 3/29/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // Any custom easing types should implement this block 12 | typedef double (^PMTweenEasingBlock)(NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration); 13 | 14 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingBack.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingBack.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/13/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweenEasing.h" 11 | 12 | /** 13 | * PMTweenEasingBack provides back easing equations. 14 | * 15 | * @warning These equations produce easing values extending beyond the starting and ending values, which may produce unpredictable results for certain properties having strict bounds limits. 16 | */ 17 | 18 | @interface PMTweenEasingBack : NSObject 19 | 20 | /** 21 | * Back easingIn type. 22 | * 23 | * @return A `PMTweenEasingBlock` block. 24 | */ 25 | + (PMTweenEasingBlock)easingIn; 26 | 27 | /** 28 | * Back easingOut type. 29 | * 30 | * @return A `PMTweenEasingBlock` block. 31 | */ 32 | + (PMTweenEasingBlock)easingOut; 33 | 34 | /** 35 | * Back easingInOut type. 36 | * 37 | * @return A `PMTweenEasingBlock` block. 38 | */ 39 | + (PMTweenEasingBlock)easingInOut; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingBack.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingBack.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/13/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "PMTweenEasingBack.h" 10 | 11 | @implementation PMTweenEasingBack 12 | 13 | #pragma mark - Easing methods 14 | 15 | + (PMTweenEasingBlock)easingIn { 16 | 17 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 18 | 19 | double overshoot = 1.70158; // defaults to 10% overshoot past the end value 20 | double time = elapsedTime / duration; 21 | 22 | double tween_value = valueDelta * time*time*((overshoot + 1.0)*time - overshoot) + startValue; 23 | 24 | return tween_value; 25 | }; 26 | 27 | return easing; 28 | 29 | } 30 | 31 | + (PMTweenEasingBlock)easingOut { 32 | 33 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 34 | 35 | double overshoot = 1.70158; // defaults to 10% overshoot past the end value 36 | double time = elapsedTime / duration - 1; 37 | 38 | double tween_value = valueDelta * (time*time * ((overshoot + 1.0)*time + overshoot) + 1.0) + startValue; 39 | 40 | return tween_value; 41 | }; 42 | 43 | return easing; 44 | 45 | } 46 | 47 | + (PMTweenEasingBlock)easingInOut { 48 | 49 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 50 | 51 | double overshoot = 1.70158; // defaults to 10% overshoot past the end value 52 | double time = elapsedTime / (duration * 0.5); 53 | overshoot *= 1.525; 54 | 55 | double tween_value = 0; 56 | if (time < 1) { 57 | tween_value = (valueDelta * 0.5) * (time*time*((overshoot + 1.0)*time - overshoot)) + startValue; 58 | } else { 59 | time -= 2.0; 60 | tween_value = (valueDelta * 0.5) * (time*time*((overshoot + 1.0)*time + overshoot) + 2.0) + startValue; 61 | } 62 | 63 | return tween_value; 64 | }; 65 | 66 | return easing; 67 | 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingBounce.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingBounce.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/14/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweenEasing.h" 11 | 12 | /** 13 | * PMTweenEasingBounce provides easing equations that have successively smaller value peaks, like a bouncing ball. 14 | */ 15 | 16 | @interface PMTweenEasingBounce : NSObject 17 | 18 | /** 19 | * Bounce easingIn type. 20 | * 21 | * @return A `PMTweenEasingBlock` block. 22 | */ 23 | + (PMTweenEasingBlock)easingIn; 24 | 25 | /** 26 | * Bounce easingOut type. 27 | * 28 | * @return A `PMTweenEasingBlock` block. 29 | */ 30 | + (PMTweenEasingBlock)easingOut; 31 | 32 | /** 33 | * Bounce easingInOut type. 34 | * 35 | * @return A `PMTweenEasingBlock` block. 36 | */ 37 | + (PMTweenEasingBlock)easingInOut; 38 | 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingBounce.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingBounce.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/14/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "PMTweenEasingBounce.h" 10 | 11 | @implementation PMTweenEasingBounce 12 | 13 | #pragma mark - Easing methods 14 | 15 | + (PMTweenEasingBlock)easingIn { 16 | 17 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 18 | 19 | PMTweenEasingBlock easingBlock = [PMTweenEasingBounce easingOut]; 20 | double tween_value = valueDelta - easingBlock(duration-elapsedTime, 0, valueDelta, duration) + startValue; 21 | 22 | return tween_value; 23 | }; 24 | 25 | return easing; 26 | 27 | } 28 | 29 | + (PMTweenEasingBlock)easingOut { 30 | 31 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 32 | 33 | double time = elapsedTime / duration; 34 | double tween_value = 0; 35 | 36 | if (time < (1/2.75)) { 37 | tween_value = valueDelta * (7.5625 * time*time) + startValue; 38 | } else if (time < (2 / 2.75)) { 39 | time -= (1.5 / 2.75); 40 | tween_value = valueDelta * (7.5625 * time*time + 0.75) + startValue; 41 | } else if (time < (2.5/2.75)) { 42 | time -= (2.25/2.75); 43 | tween_value = valueDelta * (7.5625 *time*time + 0.9375) + startValue; 44 | } else { 45 | time -= (2.625/2.75); 46 | tween_value = valueDelta * (7.5625 *time*time + 0.984375) + startValue; 47 | } 48 | 49 | 50 | return tween_value; 51 | }; 52 | 53 | return easing; 54 | 55 | } 56 | 57 | + (PMTweenEasingBlock)easingInOut { 58 | 59 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 60 | 61 | double tween_value = 0; 62 | 63 | if (elapsedTime < (duration * 0.5)) { 64 | PMTweenEasingBlock easingBlock = [PMTweenEasingBounce easingOut]; 65 | tween_value = easingBlock(elapsedTime*2, 0, valueDelta, duration) * 0.5 + startValue; 66 | } else { 67 | PMTweenEasingBlock easingBlock = [PMTweenEasingBounce easingOut]; 68 | tween_value = easingBlock(elapsedTime*2-duration, 0, valueDelta, duration) * 0.5 + (valueDelta*0.5) + startValue; 69 | } 70 | 71 | return tween_value; 72 | }; 73 | 74 | return easing; 75 | 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingCircular.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingCircular.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/13/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweenEasing.h" 11 | 12 | /** 13 | * PMTweenEasingCircular provides circular easing equations. 14 | */ 15 | 16 | @interface PMTweenEasingCircular : NSObject 17 | 18 | /** 19 | * Circular easingIn type. 20 | * 21 | * @return A `PMTweenEasingBlock` block. 22 | */ 23 | + (PMTweenEasingBlock)easingIn; 24 | 25 | /** 26 | * Circular easingOut type. 27 | * 28 | * @return A `PMTweenEasingBlock` block. 29 | */ 30 | + (PMTweenEasingBlock)easingOut; 31 | 32 | /** 33 | * Circular easingInOut type. 34 | * 35 | * @return A `PMTweenEasingBlock` block. 36 | */ 37 | + (PMTweenEasingBlock)easingInOut; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingCircular.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingCircular.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/13/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "PMTweenEasingCircular.h" 10 | 11 | @implementation PMTweenEasingCircular 12 | 13 | #pragma mark - Easing methods 14 | 15 | + (PMTweenEasingBlock)easingIn { 16 | 17 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 18 | 19 | double time = elapsedTime / duration; 20 | 21 | double tween_value = -valueDelta * (sqrt(1 - time*time) - 1) + startValue; 22 | 23 | return tween_value; 24 | }; 25 | 26 | return easing; 27 | 28 | } 29 | 30 | + (PMTweenEasingBlock)easingOut { 31 | 32 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 33 | 34 | double time = elapsedTime / duration; 35 | time--; 36 | 37 | double tween_value = valueDelta * sqrt(1 - time*time) + startValue; 38 | 39 | return tween_value; 40 | }; 41 | 42 | return easing; 43 | 44 | } 45 | 46 | + (PMTweenEasingBlock)easingInOut { 47 | 48 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 49 | 50 | double tween_value = 0; 51 | double time = elapsedTime / (duration * 0.5); 52 | if (time < 1) { 53 | tween_value = (-valueDelta * 0.5) * (sqrt(1 - time*time) - 1) + startValue; 54 | } else { 55 | time -= 2; 56 | tween_value = (valueDelta * 0.5) * (sqrt(1 - time*time) + 1) + startValue; 57 | } 58 | 59 | return tween_value; 60 | }; 61 | 62 | return easing; 63 | 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingCubic.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingCubic.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/13/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweenEasing.h" 11 | 12 | /** 13 | * PMTweenEasingCubic provides cubic easing equations. 14 | */ 15 | 16 | @interface PMTweenEasingCubic : NSObject 17 | 18 | /** 19 | * Cubic easingIn type. 20 | * 21 | * @return A `PMTweenEasingBlock` block. 22 | */ 23 | + (PMTweenEasingBlock)easingIn; 24 | 25 | /** 26 | * Cubic easingOut type. 27 | * 28 | * @return A `PMTweenEasingBlock` block. 29 | */ 30 | + (PMTweenEasingBlock)easingOut; 31 | 32 | /** 33 | * Cubic easingInOut type. 34 | * 35 | * @return A `PMTweenEasingBlock` block. 36 | */ 37 | + (PMTweenEasingBlock)easingInOut; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingCubic.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingCubic.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/13/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "PMTweenEasingCubic.h" 10 | 11 | @implementation PMTweenEasingCubic 12 | 13 | #pragma mark - Easing methods 14 | 15 | + (PMTweenEasingBlock)easingIn { 16 | 17 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 18 | 19 | double time = elapsedTime / duration; 20 | double tween_value = valueDelta * (time*time*time) + startValue; 21 | 22 | return tween_value; 23 | }; 24 | 25 | return easing; 26 | 27 | } 28 | 29 | + (PMTweenEasingBlock)easingOut { 30 | 31 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 32 | 33 | double time = elapsedTime / duration; 34 | time--; 35 | double tween_value = valueDelta * (time*time*time + 1) + startValue; 36 | 37 | return tween_value; 38 | }; 39 | 40 | return easing; 41 | 42 | } 43 | 44 | + (PMTweenEasingBlock)easingInOut { 45 | 46 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 47 | 48 | double time = elapsedTime / (duration * 0.5); 49 | 50 | double tween_value = 0; 51 | if (time < 1) { 52 | tween_value = (valueDelta * 0.5) * time*time*time + startValue; 53 | } else { 54 | time -= 2; 55 | tween_value = (valueDelta * 0.5) * (time*time*time + 2) + startValue; 56 | } 57 | 58 | return tween_value; 59 | }; 60 | 61 | return easing; 62 | 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingElastic.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingElastic.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/13/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweenEasing.h" 11 | 12 | /** 13 | * PMTweenEasingElastic provides easing equations that behave in an elastic fashion. 14 | */ 15 | 16 | @interface PMTweenEasingElastic : NSObject 17 | 18 | /** 19 | * Elastic easingIn type. 20 | * 21 | * @return A `PMTweenEasingBlock` block. 22 | */ 23 | + (PMTweenEasingBlock)easingIn; 24 | 25 | /** 26 | * Elastic easingOut type. 27 | * 28 | * @return A `PMTweenEasingBlock` block. 29 | */ 30 | + (PMTweenEasingBlock)easingOut; 31 | 32 | /** 33 | * Elastic easingInOut type. 34 | * 35 | * @return A `PMTweenEasingBlock` block. 36 | */ 37 | + (PMTweenEasingBlock)easingInOut; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingElastic.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingElastic.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/13/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "PMTweenEasingElastic.h" 10 | 11 | @implementation PMTweenEasingElastic 12 | 13 | #pragma mark - Easing methods 14 | 15 | + (PMTweenEasingBlock)easingIn { 16 | 17 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 18 | 19 | double overshoot = 1.70158; // defaults to 10% overshoot past the end value 20 | double period = 0; 21 | double amplitude = valueDelta; 22 | 23 | double tween_value = 0; 24 | double time = elapsedTime; 25 | 26 | if (time == 0) { 27 | return startValue; 28 | } 29 | 30 | time /= duration; 31 | if (time == 1) { 32 | tween_value = startValue + valueDelta; 33 | } else { 34 | if (!period) { 35 | period = duration * 0.3; 36 | } 37 | if (amplitude < fabs(valueDelta)) { 38 | amplitude = valueDelta; 39 | overshoot = period * 0.25; 40 | } else { 41 | overshoot = (period / (M_PI * 2)) * asin(valueDelta / amplitude); 42 | } 43 | 44 | time--; 45 | tween_value = -(amplitude*pow(2, 10*time) * sin( (time*duration-overshoot) * (2*M_PI)/period)) + startValue; 46 | } 47 | 48 | 49 | return tween_value; 50 | }; 51 | 52 | return easing; 53 | 54 | } 55 | 56 | + (PMTweenEasingBlock)easingOut { 57 | 58 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 59 | 60 | double overshoot = 1.70158; // defaults to 10% overshoot past the end value 61 | double period = 0; 62 | double amplitude = valueDelta; 63 | 64 | double tween_value = 0; 65 | double time = elapsedTime; 66 | 67 | if (time == 0) { 68 | return startValue; 69 | } 70 | 71 | time /= duration; 72 | if (time == 1) { 73 | tween_value = startValue + valueDelta; 74 | } else { 75 | if (!period) { 76 | period = duration * 0.3; 77 | } 78 | if (amplitude < fabs(valueDelta)) { 79 | amplitude = valueDelta; 80 | overshoot = period * 0.25; 81 | } else { 82 | overshoot = (period / (M_PI * 2)) * asin(valueDelta / amplitude); 83 | } 84 | 85 | tween_value = amplitude*pow(2,-10*time) * sin( (time*duration-overshoot) * (2*M_PI)/period ) + valueDelta + startValue; 86 | } 87 | 88 | return tween_value; 89 | }; 90 | 91 | return easing; 92 | 93 | } 94 | 95 | + (PMTweenEasingBlock)easingInOut { 96 | 97 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 98 | 99 | double overshoot = 1.70158; // defaults to 10% overshoot past the end value 100 | double period = 0; 101 | double amplitude = valueDelta; 102 | 103 | double tween_value = 0; 104 | double time = elapsedTime; 105 | 106 | if (time == 0) { 107 | return startValue; 108 | } 109 | 110 | time /= duration * 0.5; 111 | if (time == 2) { 112 | tween_value = startValue + valueDelta; 113 | } else { 114 | if (!period) { 115 | period = duration * (0.3 * 1.5); 116 | } 117 | if (amplitude < fabs(valueDelta)) { 118 | amplitude = valueDelta; 119 | overshoot = period * 0.25; 120 | } else { 121 | overshoot = (period / (M_PI * 2)) * asin(valueDelta / amplitude); 122 | } 123 | 124 | if (time < 1) { 125 | time--; 126 | tween_value = -0.5 * (amplitude*pow(2, 10*time) * sin( (time*duration-overshoot) * (2*M_PI)/period )) + startValue; 127 | } else { 128 | 129 | time--; 130 | tween_value = amplitude*pow(2, -10*time) * sin( (time*duration-overshoot) * (2*M_PI)/period ) * 0.5 + valueDelta + startValue; 131 | } 132 | 133 | } 134 | 135 | return tween_value; 136 | }; 137 | 138 | return easing; 139 | 140 | } 141 | 142 | @end 143 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingExpo.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingExpo.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/13/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweenEasing.h" 11 | 12 | /** 13 | * PMTweenEasingExpo provides exponential easing equations. 14 | */ 15 | 16 | @interface PMTweenEasingExpo : NSObject 17 | 18 | /** 19 | * Expo easingIn type. 20 | * 21 | * @return A `PMTweenEasingBlock` block. 22 | */ 23 | + (PMTweenEasingBlock)easingIn; 24 | 25 | /** 26 | * Expo easingOut type. 27 | * 28 | * @return A `PMTweenEasingBlock` block. 29 | */ 30 | + (PMTweenEasingBlock)easingOut; 31 | 32 | /** 33 | * Expo easingInOut type. 34 | * 35 | * @return A `PMTweenEasingBlock` block. 36 | */ 37 | + (PMTweenEasingBlock)easingInOut; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingExpo.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingExpo.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/13/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "PMTweenEasingExpo.h" 10 | 11 | @implementation PMTweenEasingExpo 12 | 13 | #pragma mark - Easing methods 14 | 15 | + (PMTweenEasingBlock)easingIn { 16 | 17 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 18 | 19 | double tween_value = (elapsedTime == 0) ? startValue : valueDelta * pow(2, 10 * (elapsedTime/duration - 1)) + startValue; 20 | 21 | return tween_value; 22 | }; 23 | 24 | return easing; 25 | 26 | } 27 | 28 | + (PMTweenEasingBlock)easingOut { 29 | 30 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 31 | 32 | double tween_value = (elapsedTime == duration) ? startValue+valueDelta : valueDelta * (-pow(2, -10 * elapsedTime/duration) + 1) + startValue; 33 | 34 | return tween_value; 35 | }; 36 | 37 | return easing; 38 | 39 | } 40 | 41 | + (PMTweenEasingBlock)easingInOut { 42 | 43 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 44 | 45 | double tween_value = 0; 46 | double time = elapsedTime; 47 | 48 | if (time == 0) { 49 | tween_value = startValue; 50 | 51 | } else if (time == duration) { 52 | tween_value = startValue + valueDelta; 53 | 54 | } else { 55 | time /= (duration * 0.5); 56 | if (time < 1) { 57 | tween_value = valueDelta / 2 * pow(2, 10 * (time - 1)) + startValue; 58 | 59 | } else { 60 | time--; 61 | tween_value = valueDelta / 2 * (-pow(2, -10 * time) + 2) + startValue; 62 | } 63 | } 64 | 65 | return tween_value; 66 | }; 67 | 68 | return easing; 69 | 70 | } 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingLinear.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingLinear.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 3/30/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweenEasing.h" 11 | 12 | /** 13 | * PMTweenEasingLinear provides a linear easing equation. 14 | */ 15 | 16 | @interface PMTweenEasingLinear : NSObject 17 | 18 | /** 19 | * Linear easing type. 20 | * 21 | * @return A `PMTweenEasingBlock` block. 22 | */ 23 | + (PMTweenEasingBlock)easingNone; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingLinear.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingLinear.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 3/30/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "PMTweenEasingLinear.h" 10 | 11 | @implementation PMTweenEasingLinear 12 | 13 | 14 | #pragma mark - Easing methods 15 | 16 | + (PMTweenEasingBlock)easingNone { 17 | 18 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 19 | double tween_value = valueDelta * (elapsedTime / duration) + startValue; 20 | 21 | return tween_value; 22 | }; 23 | 24 | return easing; 25 | } 26 | 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingQuadratic.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingQuadratic.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 3/30/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweenEasing.h" 11 | 12 | /** 13 | * PMTweenEasingQuadratic provides quadratic easing equations. 14 | */ 15 | 16 | @interface PMTweenEasingQuadratic : NSObject 17 | 18 | /** 19 | * Quadratic easingIn type. 20 | * 21 | * @return A `PMTweenEasingBlock` block. 22 | */ 23 | + (PMTweenEasingBlock)easingIn; 24 | 25 | /** 26 | * Quadratic easingOut type. 27 | * 28 | * @return A `PMTweenEasingBlock` block. 29 | */ 30 | + (PMTweenEasingBlock)easingOut; 31 | 32 | /** 33 | * Quadratic easingInOut type. 34 | * 35 | * @return A `PMTweenEasingBlock` block. 36 | */ 37 | + (PMTweenEasingBlock)easingInOut; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingQuadratic.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingQuadratic.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 3/30/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "PMTweenEasingQuadratic.h" 10 | 11 | 12 | @implementation PMTweenEasingQuadratic 13 | 14 | #pragma mark - Easing methods 15 | 16 | + (PMTweenEasingBlock)easingIn { 17 | 18 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 19 | double tween_value = valueDelta * (elapsedTime*elapsedTime) / (duration*duration) + startValue; 20 | 21 | return tween_value; 22 | }; 23 | 24 | return easing; 25 | 26 | } 27 | 28 | + (PMTweenEasingBlock)easingOut { 29 | 30 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 31 | double tween_value = -valueDelta * (elapsedTime*elapsedTime) / (duration*duration) + 2*valueDelta*elapsedTime/duration + startValue; 32 | 33 | return tween_value; 34 | }; 35 | 36 | return easing; 37 | 38 | } 39 | 40 | + (PMTweenEasingBlock)easingInOut { 41 | 42 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 43 | 44 | double tween_value = 0; 45 | if (elapsedTime < duration*0.5) { 46 | tween_value = 2*valueDelta * (elapsedTime*elapsedTime) / (duration*duration) + startValue; 47 | } else { 48 | double time = elapsedTime - (duration*0.5); 49 | tween_value = -2*valueDelta * (time*time)/(duration*duration) + 2*valueDelta*(time/duration) + (valueDelta*0.5) + startValue; 50 | } 51 | 52 | return tween_value; 53 | }; 54 | 55 | return easing; 56 | 57 | } 58 | 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingQuartic.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingQuartic.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/13/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweenEasing.h" 11 | 12 | /** 13 | * PMTweenEasingQuartic provides quartic easing equations. 14 | */ 15 | 16 | @interface PMTweenEasingQuartic : NSObject 17 | 18 | /** 19 | * Quartic easingIn type. 20 | * 21 | * @return A `PMTweenEasingBlock` block. 22 | */ 23 | + (PMTweenEasingBlock)easingIn; 24 | 25 | /** 26 | * Quartic easingOut type. 27 | * 28 | * @return A `PMTweenEasingBlock` block. 29 | */ 30 | + (PMTweenEasingBlock)easingOut; 31 | 32 | /** 33 | * Quartic easingInOut type. 34 | * 35 | * @return A `PMTweenEasingBlock` block. 36 | */ 37 | + (PMTweenEasingBlock)easingInOut; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingQuartic.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingQuartic.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/13/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "PMTweenEasingQuartic.h" 10 | 11 | @implementation PMTweenEasingQuartic 12 | 13 | #pragma mark - Easing methods 14 | 15 | + (PMTweenEasingBlock)easingIn { 16 | 17 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 18 | 19 | double time = elapsedTime / duration; 20 | double tween_value = valueDelta * (time*time*time*time) + startValue; 21 | 22 | return tween_value; 23 | }; 24 | 25 | return easing; 26 | 27 | } 28 | 29 | + (PMTweenEasingBlock)easingOut { 30 | 31 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 32 | 33 | double time = elapsedTime / duration; 34 | time--; 35 | double tween_value = -valueDelta * (time*time*time*time - 1) + startValue; 36 | 37 | return tween_value; 38 | }; 39 | 40 | return easing; 41 | 42 | } 43 | 44 | + (PMTweenEasingBlock)easingInOut { 45 | 46 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 47 | 48 | double time = elapsedTime / (duration * 0.5); 49 | 50 | double tween_value = 0; 51 | if (time < 1) { 52 | tween_value = (valueDelta * 0.5) * time*time*time*time + startValue; 53 | } else { 54 | time -= 2; 55 | tween_value = (-valueDelta * 0.5) * (time*time*time*time - 2) + startValue; 56 | } 57 | 58 | return tween_value; 59 | }; 60 | 61 | return easing; 62 | 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingQuintic.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingQuintic.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/13/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweenEasing.h" 11 | 12 | /** 13 | * PMTweenEasingQuintic provides quintic easing equations. 14 | */ 15 | 16 | @interface PMTweenEasingQuintic : NSObject 17 | 18 | /** 19 | * Quintic easingIn type. 20 | * 21 | * @return A `PMTweenEasingBlock` block. 22 | */ 23 | + (PMTweenEasingBlock)easingIn; 24 | 25 | /** 26 | * Quintic easingOut type. 27 | * 28 | * @return A `PMTweenEasingBlock` block. 29 | */ 30 | + (PMTweenEasingBlock)easingOut; 31 | 32 | /** 33 | * Quintic easingInOut type. 34 | * 35 | * @return A `PMTweenEasingBlock` block. 36 | */ 37 | + (PMTweenEasingBlock)easingInOut; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingQuintic.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingQuintic.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/13/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "PMTweenEasingQuintic.h" 10 | 11 | @implementation PMTweenEasingQuintic 12 | 13 | #pragma mark - Easing methods 14 | 15 | + (PMTweenEasingBlock)easingIn { 16 | 17 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 18 | 19 | double time = elapsedTime / duration; 20 | double tween_value = valueDelta * (time*time*time*time*time) + startValue; 21 | 22 | return tween_value; 23 | }; 24 | 25 | return easing; 26 | 27 | } 28 | 29 | + (PMTweenEasingBlock)easingOut { 30 | 31 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 32 | 33 | double time = elapsedTime / duration; 34 | time--; 35 | double tween_value = valueDelta * (time*time*time*time*time + 1) + startValue; 36 | 37 | return tween_value; 38 | }; 39 | 40 | return easing; 41 | 42 | } 43 | 44 | + (PMTweenEasingBlock)easingInOut { 45 | 46 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 47 | 48 | double time = elapsedTime / (duration * 0.5); 49 | 50 | double tween_value = 0; 51 | if (time < 1) { 52 | tween_value = (valueDelta * 0.5) * time*time*time*time*time + startValue; 53 | } else { 54 | time -= 2; 55 | tween_value = (valueDelta * 0.5) * (time*time*time*time*time + 2) + startValue; 56 | } 57 | 58 | return tween_value; 59 | }; 60 | 61 | return easing; 62 | 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingSine.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingSine.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/13/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweenEasing.h" 11 | 12 | /** 13 | * PMTweenEasingSine provides easing equations based on sine. 14 | */ 15 | 16 | @interface PMTweenEasingSine : NSObject 17 | 18 | /** 19 | * Sine easingIn type. 20 | * 21 | * @return A `PMTweenEasingBlock` block. 22 | */ 23 | + (PMTweenEasingBlock)easingIn; 24 | 25 | /** 26 | * Sine easingOut type. 27 | * 28 | * @return A `PMTweenEasingBlock` block. 29 | */ 30 | + (PMTweenEasingBlock)easingOut; 31 | 32 | /** 33 | * Sine easingInOut type. 34 | * 35 | * @return A `PMTweenEasingBlock` block. 36 | */ 37 | + (PMTweenEasingBlock)easingInOut; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingSine.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingSine.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/13/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "PMTweenEasingSine.h" 10 | 11 | @implementation PMTweenEasingSine 12 | 13 | #pragma mark - Easing methods 14 | 15 | + (PMTweenEasingBlock)easingIn { 16 | 17 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 18 | 19 | double tween_value = -valueDelta * cos((elapsedTime / duration) * M_PI_2) + valueDelta + startValue; 20 | 21 | return tween_value; 22 | }; 23 | 24 | return easing; 25 | 26 | } 27 | 28 | + (PMTweenEasingBlock)easingOut { 29 | 30 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 31 | 32 | double tween_value = valueDelta * sin((elapsedTime / duration) * M_PI_2) + startValue; 33 | 34 | return tween_value; 35 | }; 36 | 37 | return easing; 38 | 39 | } 40 | 41 | + (PMTweenEasingBlock)easingInOut { 42 | 43 | PMTweenEasingBlock easing = ^double (NSTimeInterval elapsedTime, double startValue, double valueDelta, NSTimeInterval duration) { 44 | 45 | double tween_value = (-valueDelta * 0.5) * (cos(M_PI * (elapsedTime / duration)) - 1) + startValue; 46 | 47 | return tween_value; 48 | }; 49 | 50 | return easing; 51 | 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /Classes/EasingTypes/PMTweenEasingTypes.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingTypes.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 1/25/16. 6 | // Copyright © 2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweenEasingLinear.h" 11 | #import "PMTweenEasingCubic.h" 12 | #import "PMTweenEasingQuadratic.h" 13 | #import "PMTweenEasingQuartic.h" 14 | #import "PMTweenEasingQuintic.h" 15 | #import "PMTweenEasingSine.h" 16 | #import "PMTweenEasingExpo.h" 17 | #import "PMTweenEasingCircular.h" 18 | #import "PMTweenEasingElastic.h" 19 | #import "PMTweenEasingBack.h" 20 | #import "PMTweenEasingBounce.h" 21 | -------------------------------------------------------------------------------- /Classes/PMTween.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTween.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/10/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweening.h" 11 | #import "PMTweenUnit.h" 12 | #import "PMTweenGroup.h" 13 | #import "PMTweenSequence.h" 14 | #import "PMTweenPhysicsUnit.h" 15 | #import "PMTweenEasingTypes.h" 16 | #import "PMTweenBeat.h" 17 | #import "PMTweenCATempo.h" 18 | -------------------------------------------------------------------------------- /Classes/PMTweenBeat.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenBeat.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 3/28/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweenTempo.h" 11 | #import "PMTween.h" 12 | 13 | /** 14 | * PMTweenBeat broadcasts updates from a PMTweenTempo object to multiple objects which adopt the `PMTweening` protocol. This allows you to use a single tempo object for all tween objects, avoiding a performance impact when tweening many objects. In most cases `PMTweenGroup` can be used for the same purpose, albeit with less cheekiness. 15 | */ 16 | 17 | @interface PMTweenBeat : NSObject { 18 | @protected 19 | NSMutableArray *_tweens; 20 | } 21 | 22 | ///------------------------------------- 23 | /// @name Creating an Instance 24 | ///------------------------------------- 25 | 26 | /** 27 | * Initializes a new PMTweenBeat object and registers the supplied PMTweenTempo. 28 | * 29 | * @param tempo A subclass of PMTweenTempo, which provides the heartbeat for PMTweenBeat. 30 | * 31 | * @warning Do not pass an instance of PMTweenTempo; instead use a subclass and implement the PMTweenTempoDelegate protocol. 32 | * 33 | * @return A new instance of this class 34 | */ 35 | - (instancetype)initWithTempo:(PMTweenTempo *)tempo; 36 | 37 | 38 | ///------------------------------------- 39 | /// @name Setting Up a Beat 40 | ///------------------------------------- 41 | 42 | /** 43 | * Adds an object to the list of objects PMTweenBeat should broadcast a tempo beat to. 44 | * 45 | * @param tween An object which adopts the `PMTweening` protocol. 46 | */ 47 | - (void)addTween:(NSObject *)tween; 48 | 49 | /** 50 | * Removes the specified object from the broadcast list. 51 | * 52 | * @param tween The tween object to remove. 53 | */ 54 | - (void)removeTween:(NSObject *)tween; 55 | 56 | /** 57 | * An array comprising the tween objects which receive tempo updates. (read-only) 58 | */ 59 | @property (readonly, nonatomic, strong) NSArray *tweens; 60 | 61 | /** 62 | * A Boolean determining whether tempo updates should be paused. 63 | */ 64 | @property (nonatomic, assign) BOOL paused; 65 | 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /Classes/PMTweenBeat.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenBeat.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 3/28/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "PMTweenBeat.h" 10 | #import "PMTweenEasing.h" 11 | #import "PMTweenCATempo.h" 12 | 13 | @interface PMTweenBeat () 14 | 15 | // Supplies the tempo to be broadcast. 16 | @property (nonatomic, strong) PMTweenTempo *tempo; 17 | 18 | @end 19 | 20 | @implementation PMTweenBeat 21 | 22 | - (instancetype)init { 23 | 24 | if (self = [super init]) { 25 | _tweens = [NSMutableArray array]; 26 | 27 | _tempo = [PMTweenCATempo tempo]; 28 | _tempo.delegate = self; 29 | 30 | } 31 | 32 | return self; 33 | } 34 | 35 | 36 | - (instancetype)initWithTempo:(PMTweenTempo *)tempo { 37 | 38 | if (self = [self init]) { 39 | _tempo = tempo; 40 | _tempo.delegate = self; 41 | 42 | } 43 | 44 | return self; 45 | } 46 | 47 | 48 | - (void)dealloc { 49 | if (_tempo) { 50 | _tempo.delegate = nil; 51 | } 52 | _tweens = nil; 53 | } 54 | 55 | 56 | - (void)addTween:(NSObject *)tween { 57 | if ([tween conformsToProtocol:@protocol(PMTweening)]) { 58 | [_tweens addObject:tween]; 59 | // remove tween's own tempo object 60 | tween.tempo = nil; 61 | } else { 62 | NSAssert(0, @"NSObject class does not conform to PMTweening protocol."); 63 | } 64 | } 65 | 66 | - (void)removeTween:(NSObject *)tween { 67 | [_tweens removeObject:tween]; 68 | } 69 | 70 | 71 | 72 | #pragma mark - setters 73 | 74 | - (void)setTweens:(NSMutableArray *)tweens { 75 | _tweens = tweens; 76 | } 77 | 78 | 79 | #pragma mark - PMTweenTempoDelegate methods 80 | 81 | - (void)tempoBeatWithTimestamp:(NSTimeInterval)timestamp { 82 | 83 | if (!_paused) { 84 | for (NSObject *tween in _tweens) { 85 | [tween updateWithTimeInterval:timestamp]; 86 | } 87 | } 88 | } 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /Classes/PMTweenCATempo.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenCATempo.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 3/28/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweenTempo.h" 11 | 12 | /** 13 | * PMTweenCATempo uses a `CADisplayLink` object to send out tempo updates that are synchronized with the refresh rate of the display. 14 | */ 15 | @interface PMTweenCATempo : PMTweenTempo 16 | 17 | ///---------------------------------- 18 | /// @name Creating an Instance 19 | ///---------------------------------- 20 | 21 | /** 22 | Convenience method for creating an instance of this class. 23 | 24 | @return An instance of PMTweenCATempo. 25 | 26 | */ 27 | + (instancetype)tempo; 28 | 29 | /** 30 | * The `CADisplayLink` object used to provide tempo updates. 31 | * 32 | * @remarks This class provides several mechanisms for adjusting the update rate. See the `CADisplayLink` documentation for more information. 33 | * 34 | * @warning Do not call the `addToRunLoop:forMode:`, `removeFromRunLoop:forMode:`, or `invalidate` methods on this object, as its state is handled by PMTweenCATempo directly. 35 | */ 36 | @property (nonatomic, strong) CADisplayLink *displayLink; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Classes/PMTweenCATempo.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenCATempo.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 3/28/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "PMTweenCATempo.h" 10 | #import 11 | 12 | @interface PMTweenCATempo () 13 | 14 | // Called by the CADisplayLink object when an update occurs. 15 | - (void)update; 16 | 17 | @end 18 | 19 | @implementation PMTweenCATempo 20 | 21 | + (instancetype)tempo { 22 | 23 | PMTweenCATempo *new_tempo = [[PMTweenCATempo alloc] init]; 24 | 25 | return new_tempo; 26 | 27 | } 28 | 29 | 30 | - (instancetype)init { 31 | if (self = [super init]) { 32 | _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)]; 33 | 34 | [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 35 | 36 | } 37 | 38 | return self; 39 | 40 | } 41 | 42 | 43 | - (void)update { 44 | 45 | NSObject *del = self.delegate; 46 | [del tempoBeatWithTimestamp:self.displayLink.timestamp]; 47 | 48 | } 49 | 50 | 51 | - (void)dealloc { 52 | [_displayLink invalidate]; 53 | } 54 | 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /Classes/PMTweenGroup.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenGroup.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/4/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweenTempo.h" 11 | #import "PMTweening.h" 12 | 13 | /** 14 | * PMTweenGroup handles the tweening of one or more objects which conform to the `PMTweening` protocol, either being instances of `PMTweenUnit` or other custom classes. The PMTweenGroup class is a good solution when you want to easily synchronize the operation of many tweens. 15 | */ 16 | 17 | @interface PMTweenGroup : NSObject { 18 | @protected 19 | BOOL _reversing; 20 | NSMutableArray *_tweens; 21 | PMTweenTempo *_tempo; 22 | } 23 | 24 | ///------------------------------------- 25 | /// @name Creating an Instance 26 | ///------------------------------------- 27 | 28 | /** 29 | * Initializes a new PMTweenGroup object and registers the provided array of tween objects. 30 | * 31 | * @param tweens An array of objects which should conform to the `PMTweening` protocol. 32 | * @param options A bitmask of `PMTweenOptions` configuration values. 33 | * 34 | * @return A new instance of this class. 35 | * 36 | * @warning A NSInternalInconsistencyException will be raised if the provided array contains an object which does not adopt the `PMTweening` protocol. 37 | * 38 | */ 39 | - (instancetype)initWithTweens:(NSArray *)tweens options:(PMTweenOptions)options; 40 | 41 | 42 | ///------------------------------------- 43 | /// @name Setting Up a Group 44 | ///------------------------------------- 45 | 46 | /** 47 | * Adds an object which conforms to the `PMTweening` protocol to the tweening group. 48 | * 49 | * @param tween An object which adopts the `PMTweening` protocol. 50 | * 51 | * @remarks All tween objects added via this method will have their `updateWithTimeInterval:currentTime:` method called by the PMTweenGroup instance. 52 | * 53 | * @warning A NSInternalInconsistencyException will be raised if the provided object does not adopt the `PMTweening` protocol. 54 | * 55 | */ 56 | - (void)addTween:(NSObject *)tween; 57 | 58 | /** 59 | * Adds an object which conforms to the `PMTweening` protocol to the tweening group. 60 | * 61 | * @param tween An object which adopts the `PMTweening` protocol. 62 | * @param useTweenTempo When `YES`, the tween object should use its own tempo to update its tween progress, and thus the `updateWithTimeInterval:currentTime:` method will not be called on the object by the PMTweenGroup instance. 63 | * 64 | * @warning A NSInternalInconsistencyException will be raised if the provided object does not adopt the `PMTweening` protocol. 65 | * 66 | */ 67 | - (void)addTween:(NSObject *)tween useTweenTempo:(BOOL)useTweenTempo; 68 | 69 | /** 70 | * Adds an array of objects which should conform to the `PMTweening` protocol to the tweening group. 71 | * 72 | * @param tweens An array of objects which should conform to the `PMTweening` protocol. 73 | * 74 | * @remarks All tween objects added via this method will have their `updateWithTimeInterval:currentTime:` method called by the PMTweenGroup instance. 75 | * 76 | * @warning A NSInternalInconsistencyException will be raised if the provided array contains an object which does not adopt the `PMTweening` protocol. 77 | * 78 | */ 79 | - (void)addTweens:(NSArray *)tweens; 80 | 81 | /** 82 | * Removes the specified tween object from the tweening group. 83 | * 84 | * @param tween The tween object to remove. 85 | */ 86 | - (void)removeTween:(NSObject *)tween; 87 | 88 | /** 89 | * The delay, in seconds, before a tween operation begins. 90 | * 91 | * @warning Setting this parameter after a tween operation has begun has no effect. 92 | */ 93 | @property (nonatomic, assign) NSTimeInterval delay; 94 | 95 | /** 96 | * A Boolean which determines whether a group's tween operation should repeat. 97 | * 98 | * @remarks When set to `YES`, each tween object in the group repeats its tween cycle for the number of times specified by the `numberOfRepeats` property. The default value is `NO`. 99 | * 100 | * @see numberOfRepeats 101 | */ 102 | @property (nonatomic, assign) BOOL repeating; 103 | 104 | /** 105 | * The number of tween cycle operations to repeat. 106 | * 107 | * @remarks This property is only used when `repeating` is set to `YES`. Negative values are clamped to 0. The default value is 0. 108 | * 109 | * @see repeating 110 | */ 111 | @property (nonatomic, assign) NSUInteger numberOfRepeats; 112 | 113 | /** 114 | * A Boolean which determines whether a group's tween objects should pause until all objects are ready to reverse directions. 115 | * 116 | * @remarks When set to `YES`, the group does not reverse direction until all objects have completed their current tween. This property only has an effect when `reversing` is set to `YES`. It posts a `PMTweenDidReverseNotification` notification when all of its tween objects are ready to reverse. When set to `NO`, its tween objects will reverse independently of each other. The default value is `YES`. 117 | * 118 | * @see reversing 119 | */ 120 | @property (nonatomic, assign) BOOL syncTweensWhenReversing; 121 | 122 | 123 | ///------------------------------------- 124 | /// @name Tween State 125 | ///------------------------------------- 126 | 127 | /** 128 | * A `PMTweenState` enum which represents the current state of the tween operation. (read-only) 129 | */ 130 | @property (readonly, nonatomic, assign) PMTweenState tweenState; 131 | 132 | /** 133 | * A `PMTweenDirection` enum which represents the current direction of the tween operation. (read-only) 134 | */ 135 | @property (readonly, nonatomic, assign) PMTweenDirection tweenDirection; 136 | 137 | /** 138 | * An array comprising the tween objects which are controlled by this PMTweenGroup object. (read-only) 139 | */ 140 | @property (readonly, nonatomic, strong) NSArray *tweens; 141 | 142 | /** 143 | * The number of completed tween cycles. (read-only) 144 | * 145 | * @remarks A cycle represents the total length of tweening operation. 146 | * 147 | * @see repeating 148 | */ 149 | @property (readonly, nonatomic, assign) NSUInteger cyclesCompletedCount; 150 | 151 | 152 | ///------------------------------------- 153 | /// @name Notification Blocks 154 | ///------------------------------------- 155 | 156 | /** 157 | * This notification block is executed when calling the `startTween` method on this instance causes a tween operation to start. 158 | * 159 | * @see startTween 160 | */ 161 | @property (nonatomic, copy) PMTweenDidStartBlock startBlock; 162 | 163 | /** 164 | * This notification block is executed when calling the `stopTween` method on this instance causes a tween operation to stop. 165 | * 166 | * @see stopTween 167 | */ 168 | @property (nonatomic, copy) PMTweenDidStopBlock stopBlock; 169 | 170 | /** 171 | * This notification block is executed when the `updateWithTimeInterval:currentTime:` method is called on this instance while this instance's `tweenState` is `PMTweenStateTweening`. 172 | * 173 | * @see updateWithTimeInterval:currentTime: 174 | */ 175 | @property (nonatomic, copy) PMTweenDidUpdateBlock updateBlock; 176 | 177 | /** 178 | * This notification block is executed when a tween cycle has completed. 179 | * 180 | * @see repeating, cyclesCompletedCount 181 | */ 182 | @property (nonatomic, copy) PMTweenDidRepeatBlock repeatCycleBlock; 183 | 184 | /** 185 | * This notification block is executed when this instance's `tweenDirection` property changes to `PMTweenDirectionReverse`. 186 | * 187 | * @see tweenDirection, reversing 188 | */ 189 | @property (nonatomic, copy) PMTweenDidReverseBlock reverseBlock; 190 | 191 | /** 192 | * This notification block is executed when calling the `pauseTween` method on this instance causes a tween operation to pause. 193 | * 194 | * @see pauseTween 195 | */ 196 | @property (nonatomic, copy) PMTweenDidPauseBlock pauseBlock; 197 | 198 | /** 199 | * This notification block is executed when calling the `resumeTween` method on this instance causes a tween operation to resume. 200 | * 201 | * @see resumeTween 202 | */ 203 | @property (nonatomic, copy) PMTweenDidResumeBlock resumeBlock; 204 | 205 | /** 206 | * This notification block is executed when a tween operation has completed (or when all tween cycles have completed, if `repeating` is set to `YES`). 207 | */ 208 | @property (nonatomic, copy) PMTweenDidCompleteBlock completeBlock; 209 | 210 | 211 | @end 212 | -------------------------------------------------------------------------------- /Classes/PMTweenObjectUpdater.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenStructUpdater.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/23/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * The `PMTweenObjectUpdating` protocol declares methods that must be adopted by a class in order to be used by PMTween to update values encoded as NSValue objects. 13 | */ 14 | @protocol PMTweenObjectUpdating 15 | 16 | /** 17 | * This method replaces an element of a NSValue object or NSObject subclass by reassigning the parent value. 18 | * 19 | * @param objectToUpdate The NSValue object that should be updated. 20 | * @param propertyValue The value of the NSValue object property to be updated. 21 | * @param propertyKeyPath A keyPath of the object's property. 22 | * 23 | * @return An updated version of the object. 24 | */ 25 | - (NSObject *)replaceObject:(NSObject *)objectToUpdate newPropertyValue:(double)propertyValue propertyKeyPath:(NSString *)propertyKeyPath; 26 | 27 | /** 28 | * This method verifies whether this class can update the specified object type. 29 | * 30 | * @param object An object to verify support for. 31 | * 32 | * @return A Boolean value representing whether the object is supported by this class. 33 | */ 34 | - (BOOL)supportsObject:(NSObject *)object; 35 | 36 | 37 | /** 38 | * A Boolean which determines whether to update a property value using additive tweening. When the value is YES, values passed in to `replaceObject:newPropertyValue:propertyKeyPath` are added to the existing value, instead of replacing it. 39 | * 40 | * @remarks The default value is NO. 41 | */ 42 | @property (nonatomic, assign) BOOL additiveUpdates; 43 | 44 | 45 | @end 46 | 47 | 48 | /** 49 | * PMTweenObjectUpdater is the default class used by `PMTweenUnit` to update data structures, either NSValue objects such as NSNumber, CGPoint, CGSize, CGRect, CGAffineTransform, or CATransform3D, and other NSObject types like UIColor. 50 | */ 51 | @interface PMTweenObjectUpdater : NSObject { 52 | @protected 53 | BOOL _additiveUpdates; 54 | } 55 | 56 | /** 57 | * Convenience method that returns a new instance of this class. 58 | * 59 | * @return A new instance of this class. 60 | */ 61 | + (instancetype)updater; 62 | 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /Classes/PMTweenPhysicsSystem.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenPhysicsSystem.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 5/1/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * The `PMTweenPhysicsSolving` protocol declares methods that must be adopted by a class in order to be used by `PMTweenPhysicsUnit` to update values using a physics system. 13 | */ 14 | @protocol PMTweenPhysicsSolving 15 | 16 | /** 17 | * The velocity value to use in physics calculations. 18 | */ 19 | @property (nonatomic, assign) double velocity; 20 | 21 | /** 22 | * The friction value to be applied in physics calculations. 23 | */ 24 | @property (nonatomic, assign) double friction; 25 | 26 | /** 27 | * This method updates a 1D position using physics calculations. 28 | * 29 | * @param position The current position of the physics object being modeled. 30 | * @param currentTime The current timestamp. 31 | * 32 | * @return An updated position. 33 | */ 34 | - (double)solveForPosition:(double)position currentTime:(NSTimeInterval)currentTime; 35 | 36 | /** 37 | * This method should reset the physics system to its initial velocity and clear the timestamp used to calculate the current step. 38 | */ 39 | - (void)resetSystem; 40 | 41 | /** 42 | * This method should reverse the direction of the velocity. 43 | */ 44 | - (void)reverseDirection; 45 | 46 | /** 47 | * This method should pause the physics system, preventing any new calculations. 48 | */ 49 | - (void)pauseSystem; 50 | 51 | /** 52 | * This method should resume the physics system. 53 | */ 54 | - (void)resumeSystem; 55 | 56 | @end 57 | 58 | 59 | /** 60 | * PMTweenPhysicsSystem is the default class used by `PMTweenPhysicsUnit` to update values. 61 | */ 62 | @interface PMTweenPhysicsSystem : NSObject { 63 | @protected 64 | double _velocity; 65 | double _initialVelocity; 66 | double _friction; 67 | } 68 | 69 | /** 70 | * Convenience method that returns a new instance of this class. 71 | * 72 | * @return A new instance of this class. 73 | */ 74 | + (instancetype)system; 75 | 76 | /** 77 | * Initializes a new PMTweenPhysicsSystem object, setting up the physics system with initial values for velocity and friction. 78 | * 79 | * @param velocity The initial velocity to use. 80 | * @param friction The friction coefficient to use. 81 | * 82 | * @return A new instance of this class. 83 | */ 84 | - (instancetype)initWithVelocity:(double)velocity friction:(double)friction; 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /Classes/PMTweenPhysicsSystem.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenPhysicsSystem.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 5/1/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "PMTweenPhysicsSystem.h" 10 | #import "PMTween.h" 11 | 12 | double static const PMTWEEN_TIMESTEP = 0.0001; 13 | 14 | @interface PMTweenPhysicsSystem () 15 | 16 | // The initial velocity value set. Used when resetting the system. 17 | @property (nonatomic, assign) double initialVelocity; 18 | 19 | // The last timestamp sent via the solveForPosition:currentTime: method. 20 | @property (nonatomic, assign) double lastTimestamp; 21 | 22 | // Boolean value representing whether the physics system is currently paused. 23 | @property (nonatomic, assign) BOOL paused; 24 | 25 | @end 26 | 27 | 28 | @implementation PMTweenPhysicsSystem 29 | 30 | + (instancetype)system { 31 | 32 | PMTweenPhysicsSystem *system = [[PMTweenPhysicsSystem alloc] init]; 33 | 34 | return system; 35 | } 36 | 37 | - (instancetype)initWithVelocity:(double)velocity friction:(double)friction { 38 | 39 | if (self = [super init]) { 40 | self.velocity = velocity; 41 | self.friction = friction; 42 | _initialVelocity = _velocity; 43 | } 44 | 45 | return self; 46 | 47 | } 48 | 49 | 50 | #pragma mark - PMTweenPhysicsSolving protocol methods 51 | 52 | - (double)solveForPosition:(double)position currentTime:(NSTimeInterval)elapsedTime { 53 | //NSLog(@"====solveForPosition=================================================="); 54 | //NSLog(@"last %f -- elapsed %f", _lastTimestamp, elapsedTime); 55 | 56 | double new_position = position; 57 | double previous_position = new_position; 58 | 59 | NSTimeInterval time_delta = elapsedTime - _lastTimestamp; 60 | time_delta = MAX(0.0, time_delta); 61 | if (time_delta > 0.2) { time_delta = 0.2; } 62 | 63 | if (!_paused && time_delta > 0.0) { 64 | if (_lastTimestamp > 0.0) { 65 | double accumulator = 0.0; 66 | 67 | accumulator += time_delta; 68 | 69 | // in this loop we apply a fixed timestep to the position solver as long as there are steps left in the time delta 70 | while (accumulator >= PMTWEEN_TIMESTEP) { 71 | previous_position = new_position; 72 | 73 | // use pow here to compensate for floating point errors over time 74 | double friction_multiplier = pow(1-_friction, PMTWEEN_TIMESTEP); 75 | 76 | _velocity *= friction_multiplier; 77 | 78 | // add just the portion of current velocity that occurred during this time delta 79 | new_position += (_velocity * PMTWEEN_TIMESTEP); 80 | 81 | // decrement the accumulator by the fixed timestep amount 82 | accumulator -= PMTWEEN_TIMESTEP; 83 | } 84 | 85 | // interpolate the remaining time delta to get the final state of position value 86 | double blending = accumulator / PMTWEEN_TIMESTEP; 87 | new_position = new_position * blending + (previous_position * (1.0 - blending)); 88 | 89 | } 90 | _lastTimestamp = elapsedTime; 91 | 92 | } 93 | 94 | return new_position; 95 | 96 | } 97 | 98 | 99 | - (void)reverseDirection { 100 | 101 | self.initialVelocity *= -1; 102 | _velocity *= -1; 103 | } 104 | 105 | 106 | - (void)resetSystem { 107 | 108 | _velocity = _initialVelocity; 109 | 110 | [self resumeSystem]; 111 | 112 | } 113 | 114 | 115 | - (void)pauseSystem { 116 | self.paused = YES; 117 | } 118 | 119 | 120 | - (void)resumeSystem { 121 | self.lastTimestamp = 0; 122 | self.paused = NO; 123 | } 124 | 125 | 126 | #pragma mark - Getter/setters 127 | 128 | - (double)velocity { 129 | return _velocity; 130 | } 131 | 132 | - (void)setVelocity:(double)velocity { 133 | 134 | _velocity = velocity; 135 | 136 | if (_initialVelocity == 0.0 || _velocity != 0.0) { 137 | self.initialVelocity = _velocity; 138 | } 139 | } 140 | 141 | - (double)friction { 142 | return _friction; 143 | } 144 | 145 | - (void)setFriction:(double)friction { 146 | // if we allowed 0, we'd get divide by zero errors 147 | // besides with 0 friction our value would sail to the stars with a constant velocity 148 | _friction = (friction > 0.0) ? friction : 0.000001; 149 | } 150 | 151 | @end 152 | -------------------------------------------------------------------------------- /Classes/PMTweenPhysicsUnit.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenPhysicsUnit.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 5/1/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweening.h" 11 | #import "PMTweenTempo.h" 12 | #import "PMTweenObjectUpdater.h" 13 | #import "PMTweenPhysicsSystem.h" 14 | 15 | /** 16 | * PMTweenPhysicsUnit handles a single tween operation on an NSValue property, using a physics system to update a value with decaying velocity. 17 | */ 18 | 19 | @interface PMTweenPhysicsUnit : NSObject { 20 | @protected 21 | BOOL _reversing; 22 | NSObject *_targetProperty; 23 | NSObject *_targetObject; 24 | PMTweenTempo *_tempo; 25 | } 26 | 27 | 28 | ///------------------------------------- 29 | /// @name Creating an Instance 30 | ///------------------------------------- 31 | 32 | /** 33 | * Initializes a new PMTweenPhysicsUnit object, passing in a property and values for the tween operation. 34 | * 35 | * @param property An NSValue property to be tweened. Supported properties include several NSValue-encoded structs such as NSNumber, CGPoint, CGSize, CGRect, CGAffineTransform, and CATransform3D. 36 | * @param startingValue The property's starting value for the tween operation. 37 | * @param velocity The velocity value to use in the physics calculations, measured in units per second. 38 | * @param friction The friction value between 0 and 1 to use in the physics calculations, with 1 representing very high friction and 0 representing almost no friction. 39 | * @param options A bitmask of `PMTweenOptions` configuration values. Defaults to `PMTweenOptionNone`. 40 | * 41 | * @return A new instance of this class. 42 | * 43 | * @remarks If you need to tween an NSValue directly, without needing to update an object such as a UIView instance, using this method is adequate. 44 | * 45 | * @see initWithObject:propertyKeyPath:startingValue:endingValue:duration:options:easingBlock: 46 | */ 47 | - (instancetype)initWithProperty:(NSValue *)property 48 | startingValue:(double)startingValue 49 | velocity:(double)velocity 50 | friction:(double)friction 51 | options:(PMTweenOptions)options; 52 | 53 | /** 54 | * Initalizes a new PMTweenPhysicsUnit object, passing in a target object, its property, and values for the tween operation. 55 | * 56 | * @param object An object whose property should be tweened. 57 | * @param propertyKeyPath A string keyPath that points to a NSValue property of the target object to be tweened. Supported properties include several NSValue-encoded structs such as NSNumber, CGPoint, CGSize, CGRect, CGAffineTransform, and CATransform3D. 58 | * @param startingValue The property's starting value for the tween operation. 59 | * @param velocity The velocity value to use in the physics calculations, measured in units per second. 60 | * @param friction The friction value between 0 and 1 to use in the physics calculations, with 1 representing very high friction and 0 representing almost no friction. 61 | * @param options A bitmask of `PMTweenOptions` configuration values. Defaults to `PMTweenOptionNone`. 62 | * 63 | * @return A new instance of this class. 64 | * 65 | * @remarks If you need to update the property of an object such as a UIView, use this method. PMTweenPhysicsUnit will handle updating the object's property automatically if you have provided a valid keyPath to the property. For a keyPath to be valid, all objects or NSValue types must be supported. By supplying a custom class to `structValueUpdater`, you can handle structs that PMTweenPhysicsUnit doesn't support by default. 66 | * 67 | * @see initWithProperty:startingValue:endingValue:duration:options:easingBlock:, structValueUpdater 68 | */ 69 | - (instancetype)initWithObject:(NSObject *)object 70 | propertyKeyPath:(NSString *)propertyKeyPath 71 | startingValue:(double)startingValue 72 | velocity:(double)velocity 73 | friction:(double)friction 74 | options:(PMTweenOptions)options; 75 | 76 | 77 | ///------------------------------------- 78 | /// @name Setting Up a Tween 79 | ///------------------------------------- 80 | 81 | /** 82 | * The delay, in seconds, before a tween operation begins. 83 | * 84 | * @warning Setting this parameter after a tween operation has begun has no effect. 85 | */ 86 | @property (nonatomic, assign) NSTimeInterval delay; 87 | 88 | /** 89 | * A Boolean which determines whether a tween operation should repeat. 90 | * 91 | * @remarks When set to `YES`, the tween operation repeats for the number of times specified by the `numberOfRepeats` property. The default value is `NO`. 92 | * 93 | * @see numberOfRepeats 94 | */ 95 | @property (nonatomic, assign) BOOL repeating; 96 | 97 | /** 98 | * The number of tween cycle operations to repeat. The default value is 0. 99 | * 100 | * @remarks This property is only used when `repeating` is set to `YES`. The default value is 0. 101 | * 102 | * @see repeating 103 | */ 104 | @property (nonatomic, assign) NSUInteger numberOfRepeats; 105 | 106 | /** 107 | * An object conforming to the `PMTweenObjectUpdating` protocol which handles the updating of properties on objects and structs. 108 | * 109 | * @remarks By default, PMTweenPhysicsUnit will assign an instance of `PMTweenObjectUpdater` to this property, but you can override this with your own custom classes if, for instance, you need to tween a value in an object or struct which `PMTweenObjectUpdater` doesn't handle. 110 | */ 111 | @property (nonatomic, strong) NSObject *structValueUpdater; 112 | 113 | /** 114 | * An object conforming to the `PMTweenPhysicsSolving` protocol which solves position calculation updates. 115 | * 116 | * @remarks By default, PMTweenPhysicsUnit will assign an instance of `PMTweenPhysicsSystem` to this property, but you can override this with your own custom classes in order to supply a different physics model. 117 | */ 118 | @property (nonatomic, strong) NSObject *physicsSystem; 119 | 120 | /** 121 | * The current velocity used by the physics system to calculate tween values, measured in units per second. 122 | * 123 | * @remarks If you wish to change the velocity after initially setting it via one of the init methods, use this setter. If you change the velocity directly on the `physicsSystem` object, the `tweenProgress` property won't be accurate. 124 | */ 125 | @property (nonatomic, assign) double velocity; 126 | 127 | /** 128 | * The current friction coefficient used by the physics system. 129 | * 130 | * @remarks A value range between 0 and 1, with 1 representing very high friction and 0 representing almost no friction. Setting this property to 0.0 will actually set it a bit fractionally higher than 0 to avoid divide-by-zero errors during calculations. 131 | */ 132 | @property (nonatomic, assign) double friction; 133 | 134 | /** 135 | * This float value is used to determine whether the object modeled by the physics system has come to rest due to deceleration. 136 | * 137 | * @remarks The way PMTweenPhysicsSystem applies friction means that as velocity approaches 0, it will be assigned smaller and smaller fractional numbers, so we need a reasonable cutoff that approximates the velocity coming to rest. The default value is the internal constant PMTWEEN_DECAY_LIMIT (set to 1.0), which is fine for display properties, but you may prefer other values. 138 | */ 139 | @property (nonatomic, assign) CGFloat velocityDecayLimit; 140 | 141 | /** 142 | * Specifies a time step for the physics solver, which is updated independently of tween value updates. The default value calls the physics system 120 times a second, which provides double the precision if your app is rendering at 60 fps. 143 | */ 144 | @property (nonatomic, assign) NSTimeInterval physicsTimerInterval; 145 | 146 | 147 | ///------------------------------------- 148 | /// @name Tween State 149 | ///------------------------------------- 150 | 151 | /** 152 | * The property to be tweened. 153 | */ 154 | @property (readonly, nonatomic, strong) NSObject *targetProperty; 155 | 156 | /** 157 | * The target object whose property should be tweened, applicable if this instance was initiated with the initWithProperty:... method. 158 | */ 159 | @property (readonly, nonatomic, strong) NSObject *targetObject; 160 | 161 | /** 162 | * A `PMTweenState` enum which represents the current state of the tween operation. (read-only) 163 | */ 164 | @property (readonly, nonatomic, assign) PMTweenState tweenState; 165 | 166 | /** 167 | * A `PMTweenDirection` enum which represents the current direction of the tween operation. (read-only) 168 | */ 169 | @property (readonly, nonatomic, assign) PMTweenDirection tweenDirection; 170 | 171 | /** 172 | * The starting value of the tween operation. 173 | * 174 | * @remarks Note that for non-numeric properties like structs this may affect multiple values, such as the x and y properties of CGPoint. 175 | * 176 | * @see currentValue, endingvalue 177 | */ 178 | @property (nonatomic, assign) double startingValue; 179 | 180 | /** 181 | * The current value of the tween operation. (read-only) 182 | * 183 | * @remarks Note that for non-numeric properties like structs this may affect multiple values, such as the x and y properties of CGPoint. 184 | * 185 | * @see startingValue, endingvalue 186 | */ 187 | @property (readonly, nonatomic, assign) double currentValue; 188 | 189 | /** 190 | * A float value between 0.0 and 1.0, which represents the current progress of a physics deceleration, calculated as the difference between the starting velocity and the current velocity. (read-only) 191 | */ 192 | @property (readonly, nonatomic, assign) CGFloat tweenProgress; 193 | 194 | /** 195 | * A float value between 0.0 and 1.0, which represents the current progress of a tween cycle. (read-only) 196 | * 197 | * @remarks This progress could represent one tween or two, depending on whether `reversing` is set to `YES`. 198 | */ 199 | @property (readonly, nonatomic, assign) CGFloat cycleProgress; 200 | 201 | /** 202 | * The amount of completed tween cycles. (read-only) 203 | * 204 | * @remarks A cycle represents the total length of tweening operation. If `reversing` is set to `YES`, a cycle comprises two separate tweens; otherwise a cycle is the length of one tween. 205 | */ 206 | @property (readonly, nonatomic, assign) NSUInteger cyclesCompletedCount; 207 | 208 | 209 | ///------------------------------------- 210 | /// @name Notification Blocks 211 | ///------------------------------------- 212 | 213 | /** 214 | * This notification block is executed when calling the `startTween` method on this instance causes a tween operation to start. 215 | * 216 | * @see startTween 217 | */ 218 | @property (nonatomic, copy) PMTweenDidStartBlock startBlock; 219 | 220 | /** 221 | * This notification block is executed when calling the `stopTween` method on this instance causes a tween operation to stop. 222 | * 223 | * @see stopTween 224 | */ 225 | @property (nonatomic, copy) PMTweenDidStopBlock stopBlock; 226 | 227 | /** 228 | * This notification block is executed when the `updateWithTimeInterval:currentTime:` method is called on this instance while this instance's `tweenState` is `PMTweenStateTweening`. 229 | * 230 | * @see updateWithTimeInterval:currentTime: 231 | */ 232 | @property (nonatomic, copy) PMTweenDidUpdateBlock updateBlock; 233 | 234 | /** 235 | * This notification block is executed when a tween cycle has repeated. 236 | * 237 | * @see repeating, cyclesCompletedCount 238 | */ 239 | @property (nonatomic, copy) PMTweenDidRepeatBlock repeatCycleBlock; 240 | 241 | /** 242 | * This notification block is executed when this instance's `tweenDirection` property changes to `PMTweenDirectionReverse`. 243 | * 244 | * @see tweenDirection, reversing 245 | */ 246 | @property (nonatomic, copy) PMTweenDidReverseBlock reverseBlock; 247 | 248 | /** 249 | * This notification block is executed when calling the `pauseTween` method on this instance causes a tween operation to pause. 250 | * 251 | * @see pauseTween 252 | */ 253 | @property (nonatomic, copy) PMTweenDidPauseBlock pauseBlock; 254 | 255 | /** 256 | * This notification block is executed when calling the `resumeTween` method on this instance causes a tween operation to resume. 257 | * 258 | * @see resumeTween 259 | */ 260 | @property (nonatomic, copy) PMTweenDidResumeBlock resumeBlock; 261 | 262 | /** 263 | * This notification block is executed when a tween operation has completed (or when all tween cycles have completed, if `repeating` is set to `YES`). 264 | */ 265 | @property (nonatomic, copy) PMTweenDidCompleteBlock completeBlock; 266 | 267 | 268 | @end 269 | -------------------------------------------------------------------------------- /Classes/PMTweenSequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenSequence.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 4/9/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweenTempo.h" 11 | #import "PMTweening.h" 12 | 13 | /** 14 | * PMTweenSequence allows objects which conform to the `PMTweening` protocol to be chained together in a sequential series of tween steps. 15 | */ 16 | 17 | @interface PMTweenSequence : NSObject { 18 | @protected 19 | BOOL _reversing; 20 | PMTweenTempo *_tempo; 21 | NSMutableArray *_sequenceSteps; 22 | } 23 | 24 | ///------------------------------------- 25 | /// @name Creating an Instance 26 | ///------------------------------------- 27 | 28 | /** 29 | * Initializes a new PMTweenSequence object, and uses the provided array of tween objects as its sequence steps. 30 | * 31 | * @param sequenceSteps An array of objects which should conform to the `PMTweening` protocol. 32 | * @param options A bitmask of `PMTweenOptions` configuration values. 33 | * 34 | * @return A new instance of this class. 35 | * 36 | * @remarks The sequence in which the objects are tweened is defined by their order in the provided array. 37 | * 38 | * @warning A NSInternalInconsistencyException will be raised if the provided array contains an object which does not adopt the `PMTweening` protocol. 39 | * 40 | */ 41 | - (instancetype)initWithSequenceSteps:(NSArray *)sequenceSteps options:(PMTweenOptions)options; 42 | 43 | 44 | ///------------------------------------- 45 | /// @name Setting Up a Sequence 46 | ///------------------------------------- 47 | 48 | /** 49 | * Adds a sequence step to the end of the sequence. 50 | * 51 | * @param sequenceStep An object which conforms to the `PMTweening` protocol. 52 | * @param useTweenTempo When `YES`, the tween object should use its own tempo to update its tween progress, and thus the `updateWithTimeInterval:currentTime:` method will not be called on the object by the PMTweenSequence instance. 53 | * 54 | * @warning A NSInternalInconsistencyException will be raised if the provided object does not adopt the `PMTweening` protocol. 55 | * 56 | */ 57 | - (void)addSequenceStep:(NSObject *)sequenceStep useTweenTempo:(BOOL)useTweenTempo; 58 | 59 | /** 60 | * Removes the specified object from the sequence. 61 | * 62 | * @param sequenceStep The sequence step to remove. 63 | */ 64 | - (void)removeSequenceStep:(NSObject *)sequenceStep; 65 | 66 | /** 67 | * The sequence step which is currently tweening, or the first sequence step if this instance's `tweenState` is `PMTweenStateStopped`. 68 | * 69 | * @return The current tween object. 70 | */ 71 | - (NSObject *)currentSequenceStep; 72 | 73 | /** 74 | * The delay, in seconds, before a sequence begins. 75 | * 76 | * @warning Setting this parameter after a sequence has begun has no effect. 77 | */ 78 | @property (nonatomic, assign) NSTimeInterval delay; 79 | 80 | /** 81 | * A Boolean which determines whether a tween operation should repeat. 82 | * 83 | * @remarks When set to `YES`, the tween operation repeats for the number of times specified by the `numberOfRepeats` property. The default value is `NO`. 84 | * 85 | * @see numberOfRepeats 86 | */ 87 | @property (nonatomic, assign) BOOL repeating; 88 | 89 | /** 90 | * The number of tween cycle operations to repeat. 91 | * 92 | * @remarks This property is only used when the sequence's `repeating` property is set to `YES`. Negative values are clamped to 0. The default value is 0. 93 | * 94 | * @see repeating 95 | */ 96 | @property (nonatomic, assign) NSUInteger numberOfRepeats; 97 | 98 | /** 99 | * The mode which defines a sequence's behavior when its `reversing` property is set to `YES`. 100 | * 101 | * @remarks The default value is `PMTweenSequenceReversingNoncontiguous`. 102 | */ 103 | @property (nonatomic, assign) PMTweenSequenceReversingMode reversingMode; 104 | 105 | 106 | ///------------------------------------- 107 | /// @name Tween State 108 | ///------------------------------------- 109 | 110 | /** 111 | * An array comprising tween objects which are controlled by this PMTweenSequence object. (read-only) 112 | * 113 | * @remarks The order of objects in this array represents the sequence order in which each will be tweened. 114 | */ 115 | @property (readonly, nonatomic, strong) NSArray *sequenceSteps; 116 | 117 | /** 118 | * A `PMTweenState` enum which represents the current state of the tween operation. (read-only) 119 | */ 120 | @property (readonly, nonatomic, assign) PMTweenState tweenState; 121 | 122 | /** 123 | * A `PMTweenDirection` enum which represents the current direction of the tween operation. (read-only) 124 | */ 125 | @property (readonly, nonatomic, assign) PMTweenDirection tweenDirection; 126 | 127 | 128 | /** 129 | * The number of completed tween cycles. (read-only) 130 | * 131 | * @remarks A cycle represents the total length of tweening operation. 132 | * 133 | * @see repeating 134 | */ 135 | @property (readonly, nonatomic, assign) NSUInteger cyclesCompletedCount; 136 | 137 | 138 | ///------------------------------------- 139 | /// @name Notification Blocks 140 | ///------------------------------------- 141 | 142 | /** 143 | * This notification block is executed when calling the `startTween` method on this instance causes a tween operation to start. 144 | * 145 | * @see startTween 146 | */ 147 | @property (nonatomic, copy) PMTweenDidStartBlock startBlock; 148 | 149 | /** 150 | * This notification block is executed when calling the `stopTween` method on this instance causes a tween operation to stop. 151 | * 152 | * @see stopTween 153 | */ 154 | @property (nonatomic, copy) PMTweenDidStopBlock stopBlock; 155 | 156 | /** 157 | * This notification block is executed when the `updateWithTimeInterval:currentTime:` method is called on this instance while this instance's `tweenState` is `PMTweenStateTweening`. 158 | * 159 | * @see updateWithTimeInterval:currentTime: 160 | */ 161 | @property (nonatomic, copy) PMTweenDidUpdateBlock updateBlock; 162 | 163 | /** 164 | * This notification block is executed when a tween cycle has completed. 165 | * 166 | * @see repeating, cyclesCompletedCount 167 | */ 168 | @property (nonatomic, copy) PMTweenDidRepeatBlock repeatCycleBlock; 169 | 170 | /** 171 | * This notification block is executed when this instance's `tweenDirection` property changes to `PMTweenDirectionReverse`. 172 | * 173 | * @see tweenDirection, reversing 174 | */ 175 | @property (nonatomic, copy) PMTweenDidReverseBlock reverseBlock; 176 | 177 | /** 178 | * This notification block is executed when calling the `pauseTween` method on this instance causes a tween operation to pause. 179 | * 180 | * @see pauseTween 181 | */ 182 | @property (nonatomic, copy) PMTweenDidPauseBlock pauseBlock; 183 | 184 | /** 185 | * This notification block is executed when calling the `resumeTween` method on this instance causes a tween operation to resume. 186 | * 187 | * @see resumeTween 188 | */ 189 | @property (nonatomic, copy) PMTweenDidResumeBlock resumeBlock; 190 | 191 | /** 192 | * This notification block is executed when a tween operation has completed (or when all tween cycles have completed, if `repeating` is set to `YES`). 193 | */ 194 | @property (nonatomic, copy) PMTweenDidCompleteBlock completeBlock; 195 | 196 | /** 197 | * This notification block is executed when playback has advanced to the next sequence step. 198 | */ 199 | typedef void (^PMTweenDidStepBlock)(NSObject *tween); 200 | @property (nonatomic, copy) PMTweenDidStepBlock stepBlock; 201 | 202 | /** 203 | * This notification is posted when playback has advanced to the next sequence step. 204 | */ 205 | extern NSString *const PMTweenDidStepNotification; 206 | 207 | 208 | 209 | 210 | 211 | @end 212 | -------------------------------------------------------------------------------- /Classes/PMTweenSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenSupport.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 1/25/16. 6 | // Copyright © 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweening.h" 11 | 12 | /** 13 | * PMTweenSupport provides some utility and internal methods for PMTween classes. 14 | */ 15 | 16 | @interface PMTweenSupport : NSObject 17 | 18 | // Holds weak references to all currently-tweening PMTweening instances which are tweening an object's property 19 | + (NSHashTable *)objectTweens; 20 | 21 | // Internal method that adds a PMTweening instance to objectTweens 22 | + (NSUInteger)addTween:(NSObject *)tween; 23 | 24 | // Internal method that removes a PMTweening instance from objectTweens 25 | + (void)removeTween:(NSObject *)tween; 26 | 27 | // Internal method that returns an incremented operation id, which are used to sort the objectTweens NSHashTable 28 | + (NSUInteger)currentTweenOperationID; 29 | 30 | // Internal method that returns the ending value of the most recent tween operation for the specified object and keyPath. If none, returns nil. 31 | + (NSValue *)targetValueForObject:(NSObject *)object keyPath:(NSString *)keyPath; 32 | 33 | // Utility method which determines whether the value is of the specified Objective-C type. 34 | + (BOOL)isValue:(NSValue *)value objCType:(const char *)typeToMatch; 35 | 36 | // Utility method that returns a getter method selector for a property name string. 37 | + (SEL)getterForPropertyName:(NSString *)propertyName; 38 | 39 | // Utility method that returns a setter method selector for a property name string. 40 | + (SEL)setterForPropertyName:(NSString *)propertyName; 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Classes/PMTweenSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenUtilities.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 1/25/16. 6 | // Copyright © 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "PMTweenSupport.h" 10 | #import "PMTweenUnit.h" 11 | 12 | @implementation PMTweenSupport 13 | 14 | #pragma mark - Utility methods 15 | 16 | + (NSHashTable *)objectTweens { 17 | 18 | static NSHashTable *tweens = nil; 19 | static dispatch_once_t only_once; 20 | dispatch_once(&only_once, ^{ 21 | tweens = [NSHashTable weakObjectsHashTable]; 22 | }); 23 | 24 | return tweens; 25 | } 26 | 27 | + (NSUInteger)addTween:(NSObject *)tween { 28 | NSHashTable *tweens = [PMTweenSupport objectTweens]; 29 | [tweens addObject:tween]; 30 | 31 | return [PMTweenSupport currentTweenOperationID]; 32 | } 33 | 34 | + (void)removeTween:(NSObject *)tween { 35 | NSHashTable *tweens = [PMTweenSupport objectTweens]; 36 | [tweens removeObject:tween]; 37 | } 38 | 39 | 40 | + (NSUInteger)currentTweenOperationID { 41 | 42 | static NSNumber *operationID = nil; 43 | static dispatch_once_t only_once; 44 | dispatch_once(&only_once, ^{ 45 | operationID = @0; 46 | }); 47 | 48 | operationID = @([operationID unsignedIntegerValue] + 1); 49 | 50 | return [operationID unsignedIntegerValue]; 51 | } 52 | 53 | 54 | + (NSValue *)targetValueForObject:(NSObject *)object keyPath:(NSString *)keyPath { 55 | 56 | __block NSValue *target_value = nil; 57 | 58 | // create an array from the operations NSSet, using the PMTweenUnit's operationID as sort key 59 | NSSet *tweens_set = [[PMTweenSupport objectTweens] setRepresentation]; 60 | NSSortDescriptor *sort_desc = [NSSortDescriptor sortDescriptorWithKey:@"operationID" ascending:YES]; 61 | NSArray *tweens = [tweens_set sortedArrayUsingDescriptors:@[sort_desc]]; 62 | 63 | // reverse through the array and find the most recent tween operation that's modifying this object property 64 | [tweens enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id tween, NSUInteger idx, BOOL *stop) { 65 | if ([tween isKindOfClass:[PMTweenUnit class]]) { 66 | PMTweenUnit *unit = (PMTweenUnit *)tween; 67 | if (unit.targetObject == object && [unit.propertyKeyPath isEqualToString:keyPath]) { 68 | target_value = [NSNumber numberWithDouble:unit.endingValue]; 69 | *stop = YES; 70 | } 71 | } 72 | }]; 73 | 74 | 75 | return target_value; 76 | } 77 | 78 | 79 | + (BOOL)isValue:(NSValue *)value objCType:(const char *)typeToMatch { 80 | BOOL is_matching = NO; 81 | 82 | const char* value_type = [value objCType]; 83 | is_matching = (strcmp(value_type, typeToMatch)==0); 84 | 85 | 86 | return is_matching; 87 | } 88 | 89 | + (SEL)getterForPropertyName:(NSString *)propertyName { 90 | SEL selector = nil; 91 | 92 | if (propertyName) { 93 | selector = NSSelectorFromString(propertyName); 94 | } 95 | 96 | return selector; 97 | } 98 | 99 | + (SEL)setterForPropertyName:(NSString *)propertyName { 100 | SEL selector = nil; 101 | 102 | if (propertyName) { 103 | NSMutableString *mutable_name = [propertyName mutableCopy]; 104 | NSString *capped_first_letter = [[propertyName substringToIndex:1] capitalizedString]; 105 | [mutable_name replaceCharactersInRange:NSMakeRange(0, 1) withString:capped_first_letter]; 106 | NSString *setter_string = [NSString stringWithFormat:@"%@%@:", @"set", mutable_name]; 107 | selector = NSSelectorFromString(setter_string); 108 | } 109 | 110 | return selector; 111 | } 112 | 113 | @end 114 | -------------------------------------------------------------------------------- /Classes/PMTweenTempo.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenTempo.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 3/28/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | /** 13 | * PMTweenTempoDelegate defines methods that are called on delegate objects that listen for tempo beats. 14 | */ 15 | @protocol PMTweenTempoDelegate 16 | 17 | /** 18 | * Sends an update beat that should prompt tweening classes to recalculate tween values. 19 | * 20 | * @param timestamp A timestamp with the current time, by which tween classes can calculate new tween values. 21 | */ 22 | - (void)tempoBeatWithTimestamp:(NSTimeInterval)timestamp; 23 | 24 | @end 25 | 26 | 27 | /** 28 | * `PMTweenTempo` is an abstract class that provides a basic structure for sending a tempo by which to update tween interpolations. Concrete subclasses should call `tempoBeatWithTimestamp:` with incremental timestamps as necessary. 29 | 30 | This class should not be instantiated directly, as it provides no tempo updates on its own. 31 | */ 32 | @interface PMTweenTempo : NSObject 33 | 34 | @property (nonatomic, weak) id delegate; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /Classes/PMTweenTempo.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenTempo.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 3/28/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "PMTweenTempo.h" 10 | 11 | @implementation PMTweenTempo 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Classes/PMTweenUnit.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenUnit.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 3/29/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweening.h" 11 | #import "PMTweenEasing.h" 12 | #import "PMTweenTempo.h" 13 | #import "PMTweenObjectUpdater.h" 14 | 15 | /** 16 | * PMTweenUnit handles a single tween operation on an NSValue property, interpolating between specified starting and ending values. 17 | */ 18 | 19 | 20 | @interface PMTweenUnit : NSObject { 21 | @protected 22 | BOOL _reversing; 23 | NSObject *_targetProperty; 24 | NSObject *_targetObject; 25 | NSString *_propertyKeyPath; 26 | PMTweenTempo *_tempo; 27 | } 28 | 29 | ///------------------------------------- 30 | /// @name Creating an Instance 31 | ///------------------------------------- 32 | 33 | /** 34 | * Initializes a new PMTweenUnit object, passing in a property and values for the tween operation. 35 | * 36 | * @param property An NSValue property to be tweened. Supported properties include several NSValue-encoded structs such as NSNumber, CGPoint, CGSize, CGRect, CGAffineTransform, and CATransform3D. 37 | * @param startingValue The property's starting value for the tween operation. 38 | * @param endingValue The property's ending value for the tween operation. 39 | * @param duration The duration of the tween. 40 | * @param options A bitmask of `PMTweenOptions` configuration values. Defaults to `PMTweenOptionNone`. 41 | * @param easingBlock An optional `PMTweenEasingBlock` block which performs easing calculations. 42 | * 43 | * @return A new instance of this class. 44 | * 45 | * @remarks If you need to tween an NSValue directly, without needing to update an object such as a UIView instance, using this method is adequate. 46 | * 47 | * @see initWithObject:propertyKeyPath:startingValue:endingValue:duration:options:easingBlock: 48 | */ 49 | - (instancetype)initWithProperty:(NSValue *)property 50 | startingValue:(double)startingValue 51 | endingValue:(double)endingValue 52 | duration:(NSTimeInterval)duration 53 | options:(PMTweenOptions)options 54 | easingBlock:(PMTweenEasingBlock)easingBlock; 55 | 56 | /** 57 | * Initalizes a new PMTweenUnit object, passing in a target object, its property, and values for the tween operation. 58 | * 59 | * @param object An object whose property should be tweened. 60 | * @param propertyKeyPath A string keyPath that points to a NSValue property of the target object to be tweened. Supported properties include several NSValue-encoded structs such as NSNumber, CGPoint, CGSize, CGRect, CGAffineTransform, and CATransform3D. 61 | * @param startingValue The property's starting value for the tween operation. 62 | * @param endingValue The property's ending value for the tween operation. 63 | * @param duration The duration of the tween. 64 | * @param options A bitmask of `PMTweenOptions` configuration values. Defaults to `PMTweenOptionNone`. 65 | * @param easingBlock An optional `PMTweenEasingBlock` block which performs easing calculations. 66 | * 67 | * @return A new instance of this class. 68 | * 69 | * @remarks If you need to update the property of an object such as a UIView, use this method. PMTweenUnit will handle updating the object's property automatically if you have provided a valid keyPath to the property. For a keyPath to be valid, all objects or NSValue types must be supported. By supplying a custom class to `structValueUpdater`, you can handle structs that PMTweenUnit doesn't support by default. 70 | * 71 | * @see initWithProperty:startingValue:endingValue:duration:options:easingBlock:, structValueUpdater 72 | */ 73 | - (instancetype)initWithObject:(NSObject *)object 74 | propertyKeyPath:(NSString *)propertyKeyPath 75 | startingValue:(double)startingValue 76 | endingValue:(double)endingValue 77 | duration:(NSTimeInterval)duration 78 | options:(PMTweenOptions)options 79 | easingBlock:(PMTweenEasingBlock)easingBlock; 80 | 81 | 82 | 83 | ///------------------------------------- 84 | /// @name Setting Up a Tween 85 | ///------------------------------------- 86 | 87 | /** 88 | * The delay, in seconds, before a tween operation begins. 89 | * 90 | * @warning Setting this parameter after a tween operation has begun has no effect. 91 | */ 92 | @property (nonatomic, assign) NSTimeInterval delay; 93 | 94 | /** 95 | * The duration of a tween operation, in seconds. (read-only) 96 | * 97 | * @remarks If `reversing` is set to `YES`, the duration of a total tween cycle will be twice this amount as there will be two separate tween operations (forwards and back). 98 | * 99 | * @warning Do not set this parameter while a tween operation is in progress. 100 | */ 101 | @property (nonatomic, assign) NSTimeInterval duration; 102 | 103 | /** 104 | * A Boolean which determines whether a tween operation should repeat. 105 | * 106 | * @remarks When set to `YES`, the tween operation repeats for the number of times specified by the `numberOfRepeats` property. The default value is `NO`. 107 | * 108 | * @see numberOfRepeats 109 | */ 110 | @property (nonatomic, assign) BOOL repeating; 111 | 112 | /** 113 | * The number of tween cycle operations to repeat. 114 | * 115 | * @remarks This property is only used when `repeating` is set to `YES`. The default value is 0. 116 | * 117 | * @see repeating 118 | */ 119 | @property (nonatomic, assign) NSUInteger numberOfRepeats; 120 | 121 | /** 122 | * A Boolean which determines whether this tween should change its property value additively. Additive animation allows multiple tweens to produce a compound effect, instead of overwriting each other as they update the same property. Additive animation is now the default behavior for tweening animations in iOS 8, and is great for making user interface animations fluid and responsive. 123 | * 124 | * @remarks The `startingValue` property is ignored for additive animations, and will instead combine with the current value of the property being tweened. Please be aware that because of the composite nature of additive animations, values can temporarily tween past the specified endingValue. This can have unintended consequences if the property you are tweening is clamped to a limited range of values. Also note that additive animations may not work well with complex tweens using sequences, reversing, or repeating, and will not work with other tweens that are not using the additive mode. Because of these caveats, the default value is NO. 125 | * 126 | */ 127 | @property (nonatomic, assign) BOOL additive; 128 | 129 | 130 | /** 131 | * An object conforming to the `PMTweenObjectUpdating` protocol which handles the updating of properties on objects and structs. 132 | * 133 | * @remarks By default, PMTweenUnit will assign an instance of `PMTweenObjectUpdater` to this property, but you can override this with your own custom classes if, for instance, you need to tween a value in an object or struct which `PMTweenObjectUpdater` doesn't handle. 134 | */ 135 | @property (nonatomic, strong) NSObject *structValueUpdater; 136 | 137 | 138 | ///------------------------------------- 139 | /// @name Easing Blocks 140 | ///------------------------------------- 141 | 142 | /** 143 | * A `PMTweenEasingBlock` block which performs easing calculations for the tweening operation. 144 | * 145 | * @see reverseEasingBlock 146 | */ 147 | @property (nonatomic, copy) PMTweenEasingBlock easingBlock; 148 | 149 | /** 150 | * An optional `PMTweenEasingBlock` block which performs easing calculations for the tweening operation while reversing. 151 | * 152 | * @remarks If not defined, the easing block defined by the `easingBlock` property is used during reversing tweens. 153 | * 154 | * @see easingBlock 155 | */ 156 | @property (nonatomic, copy) PMTweenEasingBlock reverseEasingBlock; 157 | 158 | 159 | ///------------------------------------- 160 | /// @name Tween State 161 | ///------------------------------------- 162 | 163 | /** 164 | * The property to be tweened. 165 | */ 166 | @property (readonly, nonatomic, strong) NSObject *targetProperty; 167 | 168 | /** 169 | * The target object whose property should be tweened, applicable if this instance was initiated with the initWithProperty:... method. 170 | */ 171 | @property (readonly, nonatomic, strong) NSObject *targetObject; 172 | 173 | 174 | /** 175 | * Key path for property on target. Only used when class is created with initWithObject. 176 | */ 177 | @property (readonly, nonatomic, copy) NSString *propertyKeyPath; 178 | 179 | /** 180 | * An operation ID is assigned to a PMTweenUnit when it is tweening an object's property (via initWithObject...) and its tween operation is currently in progress. (read-only) 181 | * 182 | * @remarks This value returns 0 if no ID is currently assigned. 183 | */ 184 | @property (readonly, nonatomic, assign) NSUInteger operationID; 185 | 186 | /** 187 | * A `PMTweenState` enum which represents the current state of the tween operation. (read-only) 188 | */ 189 | @property (readonly, nonatomic, assign) PMTweenState tweenState; 190 | 191 | /** 192 | * A `PMTweenDirection` enum which represents the current direction of the tween operation. (read-only) 193 | */ 194 | @property (readonly, nonatomic, assign) PMTweenDirection tweenDirection; 195 | 196 | /** 197 | * The starting value of the tween operation. 198 | * 199 | * @remarks Note that for non-numeric properties like structs this may affect multiple values, such as the x and y properties of CGPoint. 200 | * 201 | * @see currentValue, endingvalue 202 | */ 203 | @property (nonatomic, assign) double startingValue; 204 | 205 | /** 206 | * The current value of the tween operation. (read-only) 207 | * 208 | * @remarks Note that for non-numeric properties like structs this may affect multiple values, such as the x and y properties of CGPoint. 209 | * 210 | * @see startingValue, endingvalue 211 | */ 212 | @property (readonly, nonatomic, assign) double currentValue; 213 | 214 | /** 215 | * The ending value of the tween operation. 216 | * 217 | * @remarks Note that for non-numeric properties like structs this may affect multiple values, such as the x and y properties of CGPoint. 218 | */ 219 | @property (nonatomic, assign) double endingValue; 220 | 221 | /** 222 | * A float value between 0.0 and 1.0, which represents the current progress of a tween operation between two values. (read-only) 223 | */ 224 | @property (readonly, nonatomic, assign) CGFloat tweenProgress; 225 | 226 | /** 227 | * A float value between 0.0 and 1.0, which represents the current progress of a tween cycle. (read-only) 228 | * 229 | * @remarks This progress could represent one tween or two, depending on whether `reversing` is set to `YES`. 230 | */ 231 | @property (readonly, nonatomic, assign) CGFloat cycleProgress; 232 | 233 | /** 234 | * The amount of completed tween cycles. (read-only) 235 | * 236 | * @remarks A cycle represents the total length of tweening operation. If `reversing` is set to `YES`, a cycle comprises two separate tweens; otherwise a cycle is the length of one tween. 237 | */ 238 | @property (readonly, nonatomic, assign) NSUInteger cyclesCompletedCount; 239 | 240 | 241 | 242 | ///------------------------------------- 243 | /// @name Notification Blocks 244 | ///------------------------------------- 245 | 246 | /** 247 | * This notification block is executed when calling the `startTween` method on this instance causes a tween operation to start. 248 | * 249 | * @see startTween 250 | */ 251 | @property (nonatomic, copy) PMTweenDidStartBlock startBlock; 252 | 253 | /** 254 | * This notification block is executed when calling the `stopTween` method on this instance causes a tween operation to stop. 255 | * 256 | * @see stopTween 257 | */ 258 | @property (nonatomic, copy) PMTweenDidStopBlock stopBlock; 259 | 260 | /** 261 | * This notification block is executed when the `updateWithTimeInterval:currentTime:` method is called on this instance while this instance's `tweenState` is `PMTweenStateTweening`. 262 | * 263 | * @see updateWithTimeInterval:currentTime: 264 | */ 265 | @property (nonatomic, copy) PMTweenDidUpdateBlock updateBlock; 266 | 267 | /** 268 | * This notification block is executed when a tween cycle has repeated. 269 | * 270 | * @see repeating, cyclesCompletedCount 271 | */ 272 | @property (nonatomic, copy) PMTweenDidRepeatBlock repeatCycleBlock; 273 | 274 | /** 275 | * This notification block is executed when this instance's `tweenDirection` property changes to `PMTweenDirectionReverse`. 276 | * 277 | * @see tweenDirection, reversing 278 | */ 279 | @property (nonatomic, copy) PMTweenDidReverseBlock reverseBlock; 280 | 281 | /** 282 | * This notification block is executed when calling the `pauseTween` method on this instance causes a tween operation to pause. 283 | * 284 | * @see pauseTween 285 | */ 286 | @property (nonatomic, copy) PMTweenDidPauseBlock pauseBlock; 287 | 288 | /** 289 | * This notification block is executed when calling the `resumeTween` method on this instance causes a tween operation to resume. 290 | * 291 | * @see resumeTween 292 | */ 293 | @property (nonatomic, copy) PMTweenDidResumeBlock resumeBlock; 294 | 295 | /** 296 | * This notification block is executed when a tween operation has completed (or when all tween cycles have completed, if `repeating` is set to `YES`). 297 | */ 298 | @property (nonatomic, copy) PMTweenDidCompleteBlock completeBlock; 299 | 300 | 301 | @end 302 | -------------------------------------------------------------------------------- /Classes/PMTweening.h: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweening.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 1/25/16. 6 | // Copyright © 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PMTweenTempo.h" 11 | #include 12 | 13 | /** 14 | * The `PMTweening` protocol declares methods and properties that must be adopted by custom tweening classes in order to participate in PMTween. 15 | 16 | In addition to adopting the required methods, custom tweening classes must also post the `PMTweenDidCompleteNotification` notification in order to properly interact with other PMTween classes. 17 | */ 18 | 19 | @protocol PMTweening 20 | 21 | ///-------------------------- 22 | /// @name Controlling a Tween 23 | ///-------------------------- 24 | 25 | /** 26 | * Stops a tween that is currently tweening. (required) 27 | * 28 | * @remarks When this method is called, a tween should only enter a stopped state if it currently tweening. 29 | */ 30 | - (void)stopTween; 31 | 32 | /** 33 | * Starts a tween that is currently stopped. (required) 34 | * 35 | * @remarks When this method is called, a tween should only start tweening if it is stopped. 36 | */ 37 | - (void)startTween; 38 | 39 | /** 40 | * Pauses a tween that is currently tweening. (required) 41 | * 42 | * @remarks When this method is called, a tween should only enter a paused state if it is currently tweening. 43 | */ 44 | - (void)pauseTween; 45 | 46 | /** 47 | * Resumes a tween that is currently paused. (required) 48 | * 49 | * @remarks When this method is called, a tween should only resume tweening if it is currently paused. 50 | */ 51 | - (void)resumeTween; 52 | 53 | /** 54 | * A Boolean which determines whether a tween operation, when it has tweened to the ending value, should tween from the ending value back to the starting value. 55 | * 56 | * @remarks When set to `YES`, the tween plays in reverse after completing a forward tween. In this state, a tween cycle represents the combination of the forward and back tweens. The default value is `NO`. 57 | */ 58 | @property (nonatomic, assign, getter = isReversing) BOOL reversing; 59 | 60 | /** 61 | * A concrete `PMTweenTempo` subclass that provides an update "beat" while a tween operation occurs. 62 | * 63 | * @remarks While you don't have to implement PMTweenTempo for your own class updating, other tween collection classes like `PMTweenGroup` will try to remove any tempos of tween objects added to them. 64 | */ 65 | @property (nonatomic, strong) PMTweenTempo *tempo; 66 | 67 | 68 | ///----------------------- 69 | /// @name Updating a Tween 70 | ///----------------------- 71 | 72 | /** 73 | * This method is called to prompt a tweening class to update its current tween values. 74 | * 75 | * @param currentTime A timestamp that can be used in easing calculations. 76 | */ 77 | - (void)updateWithTimeInterval:(NSTimeInterval)currentTime; 78 | 79 | 80 | ///-------------------- 81 | /// @name Notifications 82 | ///-------------------- 83 | 84 | /** 85 | * Classes adopting the PMTweening protocol must post this notification when its tween operation has fully completed. 86 | * 87 | * @remarks A completion notification should only be posted when all activity related to the tween has ceased. For instance, if a tween class allows a tween to be repeated multiple times, this notification should only be posted when all repetitions have finished. 88 | */ 89 | extern NSString *const PMTweenDidCompleteNotification; 90 | 91 | /** 92 | * Classes adopting the PMTweening protocol must post this notification when its tween operation has completed half of its length. 93 | * 94 | * @remarks This notification should only be posted when half of the activity related to the tween has ceased. For instance, if a tween class is set to repeat two times and its `reversing` property is set to `YES`, it should post this notification after the second reversal of direction. 95 | */ 96 | extern NSString *const PMTweenHalfCompletedNotification; 97 | 98 | 99 | @optional 100 | 101 | /** 102 | * This notification should be posted when the `startTween` method starts a tween operation. If a delay has been specified, this notification is posted after the delay is complete. 103 | * 104 | * @see startTween 105 | */ 106 | extern NSString *const PMTweenDidStartNotification; 107 | 108 | /** 109 | * This notification should be posted when the `stopTween` method stops a tween operation. 110 | * 111 | * @see stopTween 112 | */ 113 | extern NSString *const PMTweenDidStopNotification; 114 | 115 | /** 116 | * This notification should be posted when calling the `pauseTween` method pauses a tween operation. 117 | * 118 | * @see pauseTween 119 | */ 120 | extern NSString *const PMTweenDidPauseNotification; 121 | 122 | /** 123 | * This notification should be posted when calling the `resumeTween` method resumes a tween operation. 124 | * 125 | * @see resumeTween 126 | */ 127 | extern NSString *const PMTweenDidResumeNotification; 128 | 129 | /** 130 | * This notification is posted when a tween operation reverses its tweening direction. 131 | * 132 | * @see reversing 133 | */ 134 | extern NSString *const PMTweenDidReverseNotification; 135 | 136 | /** 137 | * This notification should be posted when a tween cycle has completed. 138 | */ 139 | extern NSString *const PMTweenDidRepeatNotification; 140 | 141 | @end 142 | 143 | 144 | ///------------------------------------- 145 | /// @name Notification Blocks 146 | ///------------------------------------- 147 | 148 | /** 149 | * This notification block should be executed when the `startTween` method starts a tween operation. If a delay has been specified, this block is executed after the delay is complete. 150 | * 151 | * @see startTween 152 | */ 153 | typedef void (^PMTweenDidStartBlock)(NSObject *tween); 154 | 155 | /** 156 | * This notification block should be executed when the `stopTween` method stops a tween operation. 157 | * 158 | * @see stopTween 159 | */ 160 | typedef void (^PMTweenDidStopBlock)(NSObject *tween); 161 | 162 | /** 163 | * This notification block should be executed when the `updateWithTimeInterval:currentTime:` method is called while a tween object is currently tweening. 164 | * 165 | * @see updateWithTimeInterval:currentTime: 166 | */ 167 | typedef void (^PMTweenDidUpdateBlock)(NSObject *tween); 168 | 169 | /** 170 | * This notification block should be executed when a tween operation reverses its tweening direction. 171 | */ 172 | typedef void (^PMTweenDidReverseBlock)(NSObject *tween); 173 | 174 | /** 175 | * This notification block should be executed when a tween has repeated. 176 | */ 177 | typedef void (^PMTweenDidRepeatBlock)(NSObject *tween); 178 | 179 | /** 180 | * This notification block should be executed when calling the `pauseTween` method pauses a tween operation. 181 | * 182 | * @see pauseTween 183 | */ 184 | typedef void (^PMTweenDidPauseBlock)(NSObject *tween); 185 | 186 | /** 187 | * This notification block should be executed when calling the `resumeTween` method pauses a tween operation. 188 | * 189 | * @see resumeTween 190 | */ 191 | typedef void (^PMTweenDidResumeBlock)(NSObject *tween); 192 | 193 | /** 194 | * This notification block should be executed when a tween operation has fully completed. 195 | * 196 | * @remarks This block should only be executed when all activity related to the tween has ceased. For instance, if a tween class allows a tween to be repeated multiple times, this notification should only be posted when all repetitions have finished. 197 | */ 198 | typedef void (^PMTweenDidCompleteBlock)(NSObject *tween); 199 | 200 | 201 | ///------------------------------------- 202 | /// @name Constants 203 | ///------------------------------------- 204 | 205 | /** 206 | * Enum representing the direction a tween is tweening in. 207 | */ 208 | typedef NS_ENUM(NSInteger, PMTweenDirection) { 209 | /** 210 | * The tween is tweening in a forward direction, from the starting value to the ending value. 211 | */ 212 | PMTweenDirectionForward, 213 | /** 214 | * The tween is tweening in a reverse direction, from the ending value to the starting value. 215 | */ 216 | PMTweenDirectionReverse 217 | }; 218 | 219 | /** 220 | * Enum representing the state of a tween operation. 221 | */ 222 | typedef NS_ENUM(NSInteger, PMTweenState) { 223 | /** 224 | * The state of a tweening operation when it is tweening. 225 | */ 226 | PMTweenStateTweening, 227 | /** 228 | * The state of a tweening operation when it is stopped. 229 | */ 230 | PMTweenStateStopped, 231 | /** 232 | * The state of a tweening operation when it is paused. 233 | */ 234 | PMTweenStatePaused, 235 | /** 236 | * The state of a tweening operation when it is delayed. 237 | */ 238 | PMTweenStateDelayed 239 | }; 240 | 241 | /** 242 | * The mode used to define a sequence's behavior when its `reversing` property is set to `YES`. 243 | */ 244 | typedef NS_ENUM(NSInteger, PMTweenSequenceReversingMode) { 245 | /** 246 | * Specifies that sequence steps play in reverse when the sequence's `tweenDirection` property is `PMTweenDirectionReverse`. 247 | * 248 | * @remarks This mode is useful if you want to create a sequence whose tween steps reverse in a mirror image of their forward tweening. 249 | */ 250 | PMTweenSequenceReversingContiguous, 251 | 252 | /** 253 | * Specifies that sequence steps play forwards when the sequence's `tweenDirection` property is `PMTweenDirectionReverse`. 254 | * 255 | * @remarks This mode is useful if you want sequence steps to tween consistently, regardless of the state of the sequence's `tweenDirection` property. 256 | */ 257 | PMTweenSequenceReversingNoncontiguous 258 | }; 259 | 260 | /** 261 | * An integer bitmask providing possible initialization options for a tweening operation. 262 | */ 263 | typedef NS_OPTIONS(NSInteger, PMTweenOptions) { 264 | /** 265 | * No tween options are specified. 266 | */ 267 | PMTweenOptionNone = 0, 268 | /** 269 | * Specifies that a tween should repeat. 270 | */ 271 | PMTweenOptionRepeat = 1 << 0, 272 | /** 273 | * Specifies that a tween should reverse directions after tweening forwards. 274 | */ 275 | PMTweenOptionReverse = 1 << 1, 276 | 277 | /** 278 | * Specifies that a tween's property (or parent, if property is not KVC-compliant) should be reset on repeats or restarts. 279 | */ 280 | PMTweenOptionResetStateOnRepeat = 1 << 2 281 | }; 282 | 283 | -------------------------------------------------------------------------------- /Classes/PMTweening.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweening.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 1/25/16. 6 | // Copyright © 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "PMTweening.h" 10 | 11 | NSString *const PMTweenDidStartNotification = @"com.poetmountain.pmtween.start"; 12 | NSString *const PMTweenDidStopNotification = @"com.poetmountain.pmtween.stop"; 13 | NSString *const PMTweenDidReverseNotification = @"com.poetmountain.pmtween.reverse"; 14 | NSString *const PMTweenDidPauseNotification = @"com.poetmountain.pmtween.pause"; 15 | NSString *const PMTweenDidResumeNotification = @"com.poetmountain.pmtween.resume"; 16 | NSString *const PMTweenDidRepeatNotification = @"com.poetmountain.pmtween.repeat"; 17 | NSString *const PMTweenDidCompleteNotification = @"com.poetmountain.pmtween.complete"; 18 | NSString *const PMTweenHalfCompletedNotification = @"com.poetmountain.pmtween.halfcomplete"; 19 | 20 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 4/22/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 4/22/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "TableViewController.h" 11 | 12 | @implementation AppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 15 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 16 | 17 | TableViewController *tvc = [[TableViewController alloc] init]; 18 | UINavigationController *nav = [[UINavigationController alloc] init]; 19 | [nav addChildViewController:tvc]; 20 | [self.window setRootViewController:nav]; 21 | 22 | [self.window makeKeyAndVisible]; 23 | return YES; 24 | } 25 | 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/AdditiveVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // AdditiveVC.h 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 7/11/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AdditiveVC : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/AdditiveVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // AdditiveVC.m 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 7/11/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "AdditiveVC.h" 10 | #import "PMTweenEasingQuadratic.h" 11 | #import "PMTweenUnit.h" 12 | 13 | @interface AdditiveVC () 14 | 15 | @property (nonatomic, assign) BOOL createdUI; 16 | @property (nonatomic, strong) UIView *tweenView; 17 | @property (nonatomic, strong) PMTweenUnit *openTween; 18 | @property (nonatomic, strong) PMTweenUnit *closeTween; 19 | 20 | - (void)setupUI; 21 | - (void)setupEasing; 22 | - (void)openTouchedHandler:(id)sender; 23 | - (void)closeTouchedHandler:(id)sender; 24 | - (void)pauseTouchedHandler:(id)sender; 25 | - (void)resumeTouchedHandler:(id)sender; 26 | 27 | @end 28 | 29 | @implementation AdditiveVC 30 | 31 | - (void)viewDidLoad { 32 | [super viewDidLoad]; 33 | 34 | self.view.backgroundColor = [UIColor whiteColor]; 35 | } 36 | 37 | 38 | - (void)viewWillAppear:(BOOL)animated { 39 | [super viewWillAppear:animated]; 40 | 41 | if (!self.createdUI) { 42 | [self setupUI]; 43 | } 44 | } 45 | 46 | - (void)viewWillDisappear:(BOOL)animated { 47 | [super viewWillDisappear:animated]; 48 | 49 | [self.openTween stopTween]; 50 | [self.closeTween stopTween]; 51 | } 52 | 53 | - (void)viewDidLayoutSubviews { 54 | 55 | if (!self.createdUI) { 56 | 57 | [self setupEasing]; 58 | 59 | self.createdUI = YES; 60 | } 61 | } 62 | 63 | - (void)setupUI { 64 | 65 | self.tweenView = [[UIView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height, self.view.frame.size.width , self.view.frame.size.height)]; 66 | self.tweenView.backgroundColor = [UIColor colorWithRed:91.0/255.0 green:189.0/255.0 blue:231.0/255.0 alpha:1.0]; 67 | [self.view addSubview:self.tweenView]; 68 | 69 | CGFloat currx = 10; 70 | CGFloat btn_y = self.view.frame.size.height - 60; 71 | CGFloat btn_spacer = 10; 72 | 73 | UIButton *open_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 74 | open_btn.frame = CGRectMake(currx, btn_y, 60, 40); 75 | [open_btn setTitle:@"Open" forState:UIControlStateNormal]; 76 | [open_btn addTarget:self action:@selector(openTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 77 | [self.view addSubview:open_btn]; 78 | 79 | currx += open_btn.frame.size.width + btn_spacer; 80 | 81 | UIButton *close_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 82 | close_btn.frame = CGRectMake(currx, btn_y, 60, 40); 83 | [close_btn setTitle:@"Close" forState:UIControlStateNormal]; 84 | [close_btn addTarget:self action:@selector(closeTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 85 | [self.view addSubview:close_btn]; 86 | 87 | currx += close_btn.frame.size.width + btn_spacer; 88 | 89 | UIButton *pause_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 90 | pause_btn.frame = CGRectMake(currx, btn_y, 60, 40); 91 | [pause_btn setTitle:@"Pause" forState:UIControlStateNormal]; 92 | [pause_btn addTarget:self action:@selector(pauseTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 93 | [self.view addSubview:pause_btn]; 94 | 95 | currx += pause_btn.frame.size.width + btn_spacer; 96 | 97 | UIButton *resume_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 98 | resume_btn.frame = CGRectMake(currx, btn_y, 60, 40); 99 | [resume_btn setTitle:@"Resume" forState:UIControlStateNormal]; 100 | [resume_btn addTarget:self action:@selector(resumeTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 101 | [self.view addSubview:resume_btn]; 102 | 103 | } 104 | 105 | 106 | - (void)setupEasing { 107 | PMTweenEasingBlock easing = [PMTweenEasingQuadratic easingInOut]; 108 | 109 | self.openTween = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.origin.y" startingValue:self.tweenView.frame.origin.y endingValue:0 duration:1.6 options:PMTweenOptionNone easingBlock:easing]; 110 | self.openTween.additive = YES; 111 | self.closeTween = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.origin.y" startingValue:0 endingValue:self.view.frame.size.height duration:1.5 options:PMTweenOptionNone easingBlock:easing]; 112 | self.closeTween.additive = YES; 113 | 114 | } 115 | 116 | 117 | - (void)openTouchedHandler:(id)sender { 118 | [self.openTween startTween]; 119 | } 120 | 121 | - (void)closeTouchedHandler:(id)sender { 122 | [self.closeTween startTween]; 123 | } 124 | 125 | - (void)pauseTouchedHandler:(id)sender { 126 | [self.openTween pauseTween]; 127 | [self.closeTween pauseTween]; 128 | } 129 | 130 | - (void)resumeTouchedHandler:(id)sender { 131 | [self.openTween resumeTween]; 132 | [self.closeTween resumeTween]; 133 | } 134 | 135 | @end 136 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/BasicPhysicsTweenVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // BasicPhysicsTweenVC.h 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 5/5/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BasicPhysicsTweenVC : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/BasicPhysicsTweenVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // BasicPhysicsTweenVC.m 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 5/5/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "BasicPhysicsTweenVC.h" 10 | #import "PMTweenPhysicsUnit.h" 11 | 12 | @interface BasicPhysicsTweenVC () 13 | 14 | @property (nonatomic, assign) BOOL createdUI; 15 | @property (nonatomic, strong) UIView *tweenView; 16 | @property (nonatomic, strong) PMTweenPhysicsUnit *tween; 17 | 18 | - (void)setupUI; 19 | - (void)setupEasing; 20 | - (void)startTouchedHandler:(id)sender; 21 | - (void)stopTouchedHandler:(id)sender; 22 | - (void)pauseTouchedHandler:(id)sender; 23 | - (void)resumeTouchedHandler:(id)sender; 24 | 25 | @end 26 | 27 | @implementation BasicPhysicsTweenVC 28 | 29 | - (void)viewDidLoad { 30 | [super viewDidLoad]; 31 | 32 | self.view.backgroundColor = [UIColor whiteColor]; 33 | } 34 | 35 | 36 | - (void)viewWillAppear:(BOOL)animated { 37 | [super viewWillAppear:animated]; 38 | 39 | if (!self.createdUI) { 40 | [self setupUI]; 41 | } 42 | } 43 | 44 | - (void)viewWillDisappear:(BOOL)animated { 45 | [super viewWillDisappear:animated]; 46 | 47 | [self.tween stopTween]; 48 | } 49 | 50 | - (void)viewDidLayoutSubviews { 51 | 52 | if (!self.createdUI) { 53 | CGFloat content_top = 0; 54 | if ([self respondsToSelector:@selector(topLayoutGuide)]) { 55 | content_top = self.topLayoutGuide.length; 56 | } 57 | self.tweenView.frame = CGRectMake(20, content_top+20, 50, 50); 58 | 59 | [self setupEasing]; 60 | 61 | self.createdUI = YES; 62 | } 63 | } 64 | 65 | - (void)setupUI { 66 | 67 | self.tweenView = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 50, 50)]; 68 | self.tweenView.backgroundColor = [UIColor redColor]; 69 | [self.view addSubview:self.tweenView]; 70 | 71 | CGFloat currx = 10; 72 | CGFloat btn_y = self.view.frame.size.height - 60; 73 | CGFloat btn_spacer = 10; 74 | 75 | UIButton *start_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 76 | start_btn.frame = CGRectMake(currx, btn_y, 60, 40); 77 | [start_btn setTitle:@"Start" forState:UIControlStateNormal]; 78 | [start_btn addTarget:self action:@selector(startTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 79 | [self.view addSubview:start_btn]; 80 | 81 | currx += start_btn.frame.size.width + btn_spacer; 82 | 83 | UIButton *stop_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 84 | stop_btn.frame = CGRectMake(currx, btn_y, 60, 40); 85 | [stop_btn setTitle:@"Stop" forState:UIControlStateNormal]; 86 | [stop_btn addTarget:self action:@selector(stopTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 87 | [self.view addSubview:stop_btn]; 88 | 89 | currx += stop_btn.frame.size.width + btn_spacer; 90 | 91 | UIButton *pause_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 92 | pause_btn.frame = CGRectMake(currx, btn_y, 60, 40); 93 | [pause_btn setTitle:@"Pause" forState:UIControlStateNormal]; 94 | [pause_btn addTarget:self action:@selector(pauseTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 95 | [self.view addSubview:pause_btn]; 96 | 97 | currx += pause_btn.frame.size.width + btn_spacer; 98 | 99 | UIButton *resume_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 100 | resume_btn.frame = CGRectMake(currx, btn_y, 60, 40); 101 | [resume_btn setTitle:@"Resume" forState:UIControlStateNormal]; 102 | [resume_btn addTarget:self action:@selector(resumeTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 103 | [self.view addSubview:resume_btn]; 104 | 105 | } 106 | 107 | 108 | - (void)setupEasing { 109 | 110 | self.tween = [[PMTweenPhysicsUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.origin.x" startingValue:self.tweenView.frame.origin.x velocity:100 friction:0.4 options:PMTweenOptionNone]; 111 | 112 | __weak typeof(self) weak_self = self; 113 | self.tween.updateBlock = ^void(NSObject *tween) { 114 | //DLog(@"TWEEN 1 COMPLETE!"); 115 | __strong typeof(self) strong_self = weak_self; 116 | PMTweenPhysicsUnit *ps = (PMTweenPhysicsUnit *)tween; 117 | NSLog(@"prog %f, velocity %f, val %f", ps.tweenProgress, ps.velocity, strong_self.tweenView.frame.origin.x); 118 | }; 119 | self.tween.completeBlock = ^void(NSObject *tween) { 120 | NSLog(@"tween complete"); 121 | }; 122 | } 123 | 124 | 125 | - (void)startTouchedHandler:(id)sender { 126 | [self.tween startTween]; 127 | } 128 | 129 | - (void)stopTouchedHandler:(id)sender { 130 | [self.tween stopTween]; 131 | } 132 | 133 | - (void)pauseTouchedHandler:(id)sender { 134 | [self.tween pauseTween]; 135 | } 136 | 137 | - (void)resumeTouchedHandler:(id)sender { 138 | [self.tween resumeTween]; 139 | } 140 | 141 | @end 142 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/BasicTweenVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // BasicTweenVC.h 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 4/22/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BasicTweenVC : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/BasicTweenVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // BasicTweenVC.m 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 4/22/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "BasicTweenVC.h" 10 | #import "PMTweenEasingCubic.h" 11 | #import "PMTweenUnit.h" 12 | 13 | 14 | @interface BasicTweenVC () 15 | 16 | @property (nonatomic, assign) BOOL createdUI; 17 | @property (nonatomic, strong) UIView *tweenView; 18 | @property (nonatomic, strong) PMTweenUnit *tween; 19 | 20 | - (void)setupUI; 21 | - (void)setupEasing; 22 | - (void)startTouchedHandler:(id)sender; 23 | - (void)stopTouchedHandler:(id)sender; 24 | - (void)pauseTouchedHandler:(id)sender; 25 | - (void)resumeTouchedHandler:(id)sender; 26 | 27 | @end 28 | 29 | @implementation BasicTweenVC 30 | 31 | - (void)viewDidLoad { 32 | [super viewDidLoad]; 33 | 34 | self.view.backgroundColor = [UIColor whiteColor]; 35 | } 36 | 37 | 38 | - (void)viewWillAppear:(BOOL)animated { 39 | [super viewWillAppear:animated]; 40 | 41 | if (!self.createdUI) { 42 | [self setupUI]; 43 | } 44 | } 45 | 46 | - (void)viewWillDisappear:(BOOL)animated { 47 | [super viewWillDisappear:animated]; 48 | 49 | [self.tween stopTween]; 50 | } 51 | 52 | - (void)viewDidLayoutSubviews { 53 | 54 | if (!self.createdUI) { 55 | CGFloat content_top = 0; 56 | if ([self respondsToSelector:@selector(topLayoutGuide)]) { 57 | content_top = self.topLayoutGuide.length; 58 | } 59 | self.tweenView.frame = CGRectMake(20, content_top+20, 50, 50); 60 | 61 | [self setupEasing]; 62 | 63 | self.createdUI = YES; 64 | } 65 | } 66 | 67 | - (void)setupUI { 68 | 69 | self.tweenView = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 50, 50)]; 70 | self.tweenView.backgroundColor = [UIColor redColor]; 71 | [self.view addSubview:self.tweenView]; 72 | 73 | CGFloat currx = 10; 74 | CGFloat btn_y = self.view.frame.size.height - 60; 75 | CGFloat btn_spacer = 10; 76 | 77 | UIButton *start_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 78 | start_btn.frame = CGRectMake(currx, btn_y, 60, 40); 79 | [start_btn setTitle:@"Start" forState:UIControlStateNormal]; 80 | [start_btn addTarget:self action:@selector(startTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 81 | [self.view addSubview:start_btn]; 82 | 83 | currx += start_btn.frame.size.width + btn_spacer; 84 | 85 | UIButton *stop_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 86 | stop_btn.frame = CGRectMake(currx, btn_y, 60, 40); 87 | [stop_btn setTitle:@"Stop" forState:UIControlStateNormal]; 88 | [stop_btn addTarget:self action:@selector(stopTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 89 | [self.view addSubview:stop_btn]; 90 | 91 | currx += stop_btn.frame.size.width + btn_spacer; 92 | 93 | UIButton *pause_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 94 | pause_btn.frame = CGRectMake(currx, btn_y, 60, 40); 95 | [pause_btn setTitle:@"Pause" forState:UIControlStateNormal]; 96 | [pause_btn addTarget:self action:@selector(pauseTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 97 | [self.view addSubview:pause_btn]; 98 | 99 | currx += pause_btn.frame.size.width + btn_spacer; 100 | 101 | UIButton *resume_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 102 | resume_btn.frame = CGRectMake(currx, btn_y, 60, 40); 103 | [resume_btn setTitle:@"Resume" forState:UIControlStateNormal]; 104 | [resume_btn addTarget:self action:@selector(resumeTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 105 | [self.view addSubview:resume_btn]; 106 | 107 | } 108 | 109 | 110 | - (void)setupEasing { 111 | PMTweenEasingBlock easing = [PMTweenEasingCubic easingInOut]; 112 | 113 | self.tween = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.origin.x" startingValue:self.tweenView.frame.origin.x endingValue:200 duration:1.2 options:PMTweenOptionNone easingBlock:easing]; 114 | } 115 | 116 | 117 | - (void)startTouchedHandler:(id)sender { 118 | [self.tween startTween]; 119 | } 120 | 121 | - (void)stopTouchedHandler:(id)sender { 122 | [self.tween stopTween]; 123 | } 124 | 125 | - (void)pauseTouchedHandler:(id)sender { 126 | [self.tween pauseTween]; 127 | } 128 | 129 | - (void)resumeTouchedHandler:(id)sender { 130 | [self.tween resumeTween]; 131 | } 132 | 133 | @end 134 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/DynamicTweenVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // DynamicTweenVC.h 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 5/5/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DynamicTweenVC : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/DynamicTweenVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // DynamicTweenVC.m 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 5/5/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "DynamicTweenVC.h" 10 | #import "PMTweenUnit.h" 11 | #import "PMTweenGroup.h" 12 | #import "PMTweenEasingQuadratic.h" 13 | 14 | @interface DynamicTweenVC () 15 | 16 | @property (nonatomic, assign) BOOL createdUI; 17 | @property (nonatomic, strong) UIView *tweenView; 18 | @property (nonatomic, strong) NSMutableArray *tweens; 19 | @property (nonatomic, strong) UITapGestureRecognizer *tapRecognizer; 20 | 21 | - (void)setupUI; 22 | - (void)startTouchedHandler:(id)sender; 23 | - (void)stopTouchedHandler:(id)sender; 24 | - (void)pauseTouchedHandler:(id)sender; 25 | - (void)resumeTouchedHandler:(id)sender; 26 | - (void)viewTappedHandler:(UIGestureRecognizer *)gesture; 27 | 28 | @end 29 | 30 | @implementation DynamicTweenVC 31 | 32 | - (void)viewDidLoad { 33 | [super viewDidLoad]; 34 | 35 | self.view.backgroundColor = [UIColor whiteColor]; 36 | } 37 | 38 | 39 | - (void)viewWillAppear:(BOOL)animated { 40 | [super viewWillAppear:animated]; 41 | 42 | if (!self.createdUI) { 43 | [self setupUI]; 44 | 45 | self.tweens = [NSMutableArray array]; 46 | 47 | self.tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(viewTappedHandler:)]; 48 | [self.view addGestureRecognizer:self.tapRecognizer]; 49 | } 50 | 51 | } 52 | 53 | - (void)viewWillDisappear:(BOOL)animated { 54 | [super viewWillDisappear:animated]; 55 | 56 | for (PMTweenUnit *unit in self.tweens) { 57 | [unit stopTween]; 58 | } 59 | } 60 | 61 | - (void)viewDidLayoutSubviews { 62 | 63 | if (!self.createdUI) { 64 | CGFloat content_top = 0; 65 | if ([self respondsToSelector:@selector(topLayoutGuide)]) { 66 | content_top = self.topLayoutGuide.length; 67 | } 68 | self.tweenView.frame = CGRectMake(20, content_top+20, 50, 50); 69 | 70 | self.createdUI = YES; 71 | } 72 | } 73 | 74 | - (void)setupUI { 75 | 76 | UILabel *instruct = [[UILabel alloc] initWithFrame:CGRectMake(80, 80, 200, 40)]; 77 | instruct.font = [UIFont systemFontOfSize:12]; 78 | instruct.textColor = [UIColor blackColor]; 79 | instruct.backgroundColor = [UIColor whiteColor]; 80 | instruct.userInteractionEnabled = NO; 81 | instruct.numberOfLines = 2; 82 | [instruct setText:@"Tap on background to change the tween destination."]; 83 | [self.view addSubview:instruct]; 84 | 85 | self.tweenView = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 50, 50)]; 86 | self.tweenView.backgroundColor = [UIColor colorWithRed:76.0/255.0 green:164.0/255.0 blue:68.0/255.0 alpha:1.0]; 87 | [self.view addSubview:self.tweenView]; 88 | 89 | CGFloat currx = 10; 90 | CGFloat btn_y = self.view.frame.size.height - 60; 91 | CGFloat btn_spacer = 10; 92 | 93 | UIButton *pause_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 94 | pause_btn.frame = CGRectMake(currx, btn_y, 60, 40); 95 | [pause_btn setTitle:@"Pause" forState:UIControlStateNormal]; 96 | [pause_btn addTarget:self action:@selector(pauseTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 97 | [self.view addSubview:pause_btn]; 98 | 99 | currx += pause_btn.frame.size.width + btn_spacer; 100 | 101 | UIButton *resume_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 102 | resume_btn.frame = CGRectMake(currx, btn_y, 60, 40); 103 | [resume_btn setTitle:@"Resume" forState:UIControlStateNormal]; 104 | [resume_btn addTarget:self action:@selector(resumeTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 105 | [self.view addSubview:resume_btn]; 106 | 107 | } 108 | 109 | 110 | 111 | - (void)viewTappedHandler:(UIGestureRecognizer *)gesture { 112 | 113 | if (gesture.state != UIGestureRecognizerStateEnded) { 114 | return; 115 | } 116 | 117 | CGPoint point = [gesture locationInView:self.view]; 118 | //CGFloat slope_x = (point.x - self.tweenView.center.x) / self.view.frame.size.width; 119 | //CGFloat slope_y = (point.y - self.tweenView.center.y) / self.view.frame.size.height; 120 | 121 | __weak typeof(self) weak_self = self; 122 | 123 | PMTweenEasingBlock easing = [PMTweenEasingQuadratic easingInOut]; 124 | PMTweenUnit *tapx = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.origin.x" startingValue:self.tweenView.center.x endingValue:point.x duration:1.5 options:PMTweenOptionNone easingBlock:easing]; 125 | tapx.additive = YES; 126 | tapx.completeBlock = ^void(NSObject *tween) { 127 | __strong typeof(self) strong_self = weak_self; 128 | [strong_self.tweens removeObject:tween]; 129 | }; 130 | [self.tweens addObject:tapx]; 131 | PMTweenUnit *tapy = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.origin.y" startingValue:self.tweenView.center.y endingValue:point.y duration:1.5 options:PMTweenOptionNone easingBlock:easing]; 132 | tapy.additive = YES; 133 | tapy.completeBlock = ^void(NSObject *tween) { 134 | __strong typeof(self) strong_self = weak_self; 135 | [strong_self.tweens removeObject:tween]; 136 | }; 137 | [self.tweens addObject:tapy]; 138 | 139 | [tapx startTween]; 140 | [tapy startTween]; 141 | 142 | } 143 | 144 | 145 | - (void)startTouchedHandler:(id)sender { 146 | 147 | } 148 | 149 | - (void)stopTouchedHandler:(id)sender { 150 | for (PMTweenUnit *unit in self.tweens) { 151 | [unit stopTween]; 152 | } 153 | [self.tweens removeAllObjects]; 154 | } 155 | 156 | - (void)pauseTouchedHandler:(id)sender { 157 | for (PMTweenUnit *unit in self.tweens) { 158 | [unit pauseTween]; 159 | } 160 | 161 | self.tapRecognizer.enabled = NO; 162 | } 163 | 164 | - (void)resumeTouchedHandler:(id)sender { 165 | for (PMTweenUnit *unit in self.tweens) { 166 | [unit resumeTween]; 167 | } 168 | 169 | self.tapRecognizer.enabled = YES; 170 | } 171 | 172 | @end 173 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/GroupTweenVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // GroupTweenVC.h 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 4/22/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GroupTweenVC : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/GroupTweenVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // GroupTweenVC.m 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 4/22/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "GroupTweenVC.h" 10 | #import "PMTweenCATempo.h" 11 | #import "PMTweenEasingCubic.h" 12 | #import "PMTweenEasingSine.h" 13 | #import "PMTweenUnit.h" 14 | #import "PMTweenGroup.h" 15 | 16 | @interface GroupTweenVC () 17 | 18 | @property (nonatomic, assign) BOOL createdUI; 19 | @property (nonatomic, strong) UIView *tweenView; 20 | @property (nonatomic, strong) PMTweenGroup *group; 21 | 22 | - (void)setupUI; 23 | - (void)setupTweens; 24 | - (void)startTouchedHandler:(id)sender; 25 | - (void)stopTouchedHandler:(id)sender; 26 | - (void)pauseTouchedHandler:(id)sender; 27 | - (void)resumeTouchedHandler:(id)sender; 28 | 29 | @end 30 | 31 | @implementation GroupTweenVC 32 | 33 | - (void)viewDidLoad { 34 | [super viewDidLoad]; 35 | 36 | self.view.backgroundColor = [UIColor whiteColor]; 37 | } 38 | 39 | - (void)viewWillAppear:(BOOL)animated { 40 | [super viewWillAppear:animated]; 41 | 42 | if (!self.createdUI) { 43 | [self setupUI]; 44 | } 45 | } 46 | 47 | - (void)viewWillDisappear:(BOOL)animated { 48 | [super viewWillDisappear:animated]; 49 | 50 | [self.group stopTween]; 51 | } 52 | 53 | - (void)viewDidLayoutSubviews { 54 | 55 | if (!self.createdUI) { 56 | CGFloat content_top = 0; 57 | if ([self respondsToSelector:@selector(topLayoutGuide)]) { 58 | content_top = self.topLayoutGuide.length; 59 | } 60 | self.tweenView.frame = CGRectMake(20, content_top+20, 50, 50); 61 | 62 | [self setupTweens]; 63 | 64 | self.createdUI = YES; 65 | } 66 | } 67 | 68 | - (void)setupUI { 69 | 70 | self.tweenView = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 50, 50)]; 71 | self.tweenView.backgroundColor = [UIColor redColor]; 72 | [self.view addSubview:self.tweenView]; 73 | 74 | CGFloat currx = 10; 75 | CGFloat btn_y = self.view.frame.size.height - 60; 76 | CGFloat btn_spacer = 10; 77 | 78 | UIButton *start_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 79 | start_btn.frame = CGRectMake(currx, btn_y, 60, 40); 80 | [start_btn setTitle:@"Start" forState:UIControlStateNormal]; 81 | [start_btn addTarget:self action:@selector(startTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 82 | [self.view addSubview:start_btn]; 83 | 84 | currx += start_btn.frame.size.width + btn_spacer; 85 | 86 | UIButton *stop_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 87 | stop_btn.frame = CGRectMake(currx, btn_y, 60, 40); 88 | [stop_btn setTitle:@"Stop" forState:UIControlStateNormal]; 89 | [stop_btn addTarget:self action:@selector(stopTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 90 | [self.view addSubview:stop_btn]; 91 | 92 | currx += stop_btn.frame.size.width + btn_spacer; 93 | 94 | UIButton *pause_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 95 | pause_btn.frame = CGRectMake(currx, btn_y, 60, 40); 96 | [pause_btn setTitle:@"Pause" forState:UIControlStateNormal]; 97 | [pause_btn addTarget:self action:@selector(pauseTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 98 | [self.view addSubview:pause_btn]; 99 | 100 | currx += pause_btn.frame.size.width + btn_spacer; 101 | 102 | UIButton *resume_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 103 | resume_btn.frame = CGRectMake(currx, btn_y, 60, 40); 104 | [resume_btn setTitle:@"Resume" forState:UIControlStateNormal]; 105 | [resume_btn addTarget:self action:@selector(resumeTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 106 | [self.view addSubview:resume_btn]; 107 | } 108 | 109 | 110 | - (void)setupTweens { 111 | 112 | PMTweenEasingBlock easing_cubic = [PMTweenEasingCubic easingInOut]; 113 | PMTweenEasingBlock easing_y = [PMTweenEasingSine easingOut]; 114 | 115 | PMTweenUnit *tween = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.origin.x" startingValue:self.tweenView.frame.origin.x endingValue:200 duration:1.0 options:PMTweenOptionNone easingBlock:easing_cubic]; 116 | PMTweenUnit *tween2 = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.origin.y" startingValue:self.tweenView.frame.origin.y endingValue:290 duration:1.25 options:PMTweenOptionNone easingBlock:easing_y]; 117 | PMTweenUnit *tween3 = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"backgroundColor.green" startingValue:0.0 endingValue:1 duration:1.0 options:PMTweenOptionNone easingBlock:easing_y]; 118 | 119 | self.group = [[PMTweenGroup alloc] initWithTweens:@[tween, tween2, tween3] options:PMTweenOptionReverse]; 120 | } 121 | 122 | 123 | - (void)startTouchedHandler:(id)sender { 124 | [self.group startTween]; 125 | } 126 | 127 | - (void)stopTouchedHandler:(id)sender { 128 | [self.group stopTween]; 129 | } 130 | 131 | - (void)pauseTouchedHandler:(id)sender { 132 | [self.group pauseTween]; 133 | } 134 | 135 | - (void)resumeTouchedHandler:(id)sender { 136 | [self.group resumeTween]; 137 | } 138 | 139 | @end 140 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/MassTweensVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // MassTweensVC.h 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 4/23/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MassTweensVC : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/MassTweensVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // MassTweensVC.m 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 4/23/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "MassTweensVC.h" 10 | #import "PMTweenCATempo.h" 11 | #import "PMTweenEasingCubic.h" 12 | #import "PMTweenUnit.h" 13 | #import "PMTweenGroup.h" 14 | 15 | @interface MassTweensVC () 16 | 17 | @property (nonatomic, assign) BOOL createdUI; 18 | @property (nonatomic, strong) PMTweenGroup *group; 19 | 20 | - (void)setupUI; 21 | - (void)setupEasing; 22 | - (void)startTouchedHandler:(id)sender; 23 | - (void)stopTouchedHandler:(id)sender; 24 | - (void)pauseTouchedHandler:(id)sender; 25 | - (void)resumeTouchedHandler:(id)sender; 26 | 27 | @end 28 | 29 | @implementation MassTweensVC 30 | 31 | - (void)viewDidLoad { 32 | [super viewDidLoad]; 33 | 34 | self.view.backgroundColor = [UIColor whiteColor]; 35 | } 36 | 37 | 38 | - (void)viewWillAppear:(BOOL)animated { 39 | [super viewWillAppear:animated]; 40 | 41 | if (!self.createdUI) { 42 | [self setupUI]; 43 | } 44 | } 45 | 46 | - (void)viewWillDisappear:(BOOL)animated { 47 | [super viewWillDisappear:animated]; 48 | 49 | [self.group stopTween]; 50 | } 51 | 52 | - (void)viewDidLayoutSubviews { 53 | 54 | if (!self.createdUI) { 55 | [self setupEasing]; 56 | self.createdUI = YES; 57 | } 58 | } 59 | 60 | 61 | - (void)setupUI { 62 | 63 | CGFloat currx = 10; 64 | CGFloat btn_y = self.view.frame.size.height - 60; 65 | CGFloat btn_spacer = 10; 66 | 67 | UIButton *start_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 68 | start_btn.frame = CGRectMake(currx, btn_y, 60, 40); 69 | [start_btn setTitle:@"Start" forState:UIControlStateNormal]; 70 | [start_btn addTarget:self action:@selector(startTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 71 | [self.view addSubview:start_btn]; 72 | 73 | currx += start_btn.frame.size.width + btn_spacer; 74 | 75 | UIButton *stop_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 76 | stop_btn.frame = CGRectMake(currx, btn_y, 60, 40); 77 | [stop_btn setTitle:@"Stop" forState:UIControlStateNormal]; 78 | [stop_btn addTarget:self action:@selector(stopTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 79 | [self.view addSubview:stop_btn]; 80 | 81 | currx += stop_btn.frame.size.width + btn_spacer; 82 | 83 | UIButton *pause_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 84 | pause_btn.frame = CGRectMake(currx, btn_y, 60, 40); 85 | [pause_btn setTitle:@"Pause" forState:UIControlStateNormal]; 86 | [pause_btn addTarget:self action:@selector(pauseTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 87 | [self.view addSubview:pause_btn]; 88 | 89 | currx += pause_btn.frame.size.width + btn_spacer; 90 | 91 | UIButton *resume_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 92 | resume_btn.frame = CGRectMake(currx, btn_y, 60, 40); 93 | [resume_btn setTitle:@"Resume" forState:UIControlStateNormal]; 94 | [resume_btn addTarget:self action:@selector(resumeTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 95 | [self.view addSubview:resume_btn]; 96 | 97 | 98 | } 99 | 100 | 101 | - (void)setupEasing { 102 | PMTweenEasingBlock easing = [PMTweenEasingCubic easingInOut]; 103 | 104 | NSMutableArray *tweens = [NSMutableArray array]; 105 | 106 | CGFloat content_top = 0; 107 | if ([self respondsToSelector:@selector(topLayoutGuide)]) { 108 | content_top = self.topLayoutGuide.length; 109 | } 110 | 111 | for (NSInteger i=0; i<250; i++) { 112 | NSInteger x = 10 + arc4random_uniform(self.view.frame.size.width-40); 113 | NSInteger y = content_top + arc4random_uniform(self.view.frame.size.height-150); 114 | 115 | UIView *tween_view = [[UIView alloc] initWithFrame:CGRectMake(x, y, 10, 10)]; 116 | tween_view.backgroundColor = [UIColor colorWithRed:arc4random_uniform(255)/255.0 green:arc4random_uniform(255)/255.0 blue:arc4random_uniform(255)/255.0 alpha:1.0]; 117 | [self.view addSubview:tween_view]; 118 | 119 | PMTweenUnit *tweenx = [[PMTweenUnit alloc] initWithObject:tween_view propertyKeyPath:@"frame.origin.x" startingValue:tween_view.frame.origin.x endingValue:10+arc4random_uniform(self.view.frame.size.width-20) duration:2.0 options:PMTweenOptionNone easingBlock:easing]; 120 | tweenx.completeBlock = ^void(NSObject *tween) { 121 | PMTweenUnit *unit = (PMTweenUnit *)tween; 122 | unit.endingValue = 10+arc4random_uniform(self.view.frame.size.width-20); 123 | }; 124 | 125 | PMTweenUnit *tweeny = [[PMTweenUnit alloc] initWithObject:tween_view propertyKeyPath:@"frame.origin.y" startingValue:tween_view.frame.origin.y endingValue:content_top+arc4random_uniform(self.view.frame.size.height-150) duration:2.0 options:PMTweenOptionNone easingBlock:easing]; 126 | tweeny.completeBlock = ^void(NSObject *tween) { 127 | PMTweenUnit *unit = (PMTweenUnit *)tween; 128 | unit.endingValue = content_top+arc4random_uniform(self.view.frame.size.height-150); 129 | }; 130 | 131 | PMTweenGroup *xy = [[PMTweenGroup alloc] initWithTweens:@[tweenx, tweeny] options:PMTweenOptionNone]; 132 | [tweens addObject:xy]; 133 | } 134 | 135 | self.group = [[PMTweenGroup alloc] initWithTweens:tweens options:PMTweenOptionRepeat|PMTweenOptionReverse]; 136 | self.group.numberOfRepeats = NSIntegerMax; 137 | 138 | } 139 | 140 | 141 | - (void)startTouchedHandler:(id)sender { 142 | [self.group startTween]; 143 | } 144 | 145 | - (void)stopTouchedHandler:(id)sender { 146 | [self.group stopTween]; 147 | } 148 | 149 | - (void)pauseTouchedHandler:(id)sender { 150 | [self.group pauseTween]; 151 | } 152 | 153 | - (void)resumeTouchedHandler:(id)sender { 154 | [self.group resumeTween]; 155 | } 156 | 157 | @end 158 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/SequenceNoncontiguousVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // SequenceNoncontiguousVC.h 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 4/22/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SequenceNoncontiguousVC : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/SequenceNoncontiguousVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // SequenceNoncontiguousVC.m 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 4/22/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "SequenceNoncontiguousVC.h" 10 | #import "PMTweenCATempo.h" 11 | #import "PMTweenEasingCubic.h" 12 | #import "PMTweenUnit.h" 13 | #import "PMTweenSequence.h" 14 | 15 | @interface SequenceNoncontiguousVC () 16 | 17 | @property (nonatomic, assign) BOOL createdUI; 18 | @property (nonatomic, strong) UIView *view1; 19 | @property (nonatomic, strong) UIView *view2; 20 | @property (nonatomic, strong) UIView *view3; 21 | @property (nonatomic, strong) UIView *view4; 22 | 23 | @property (nonatomic, strong) PMTweenSequence *sequence; 24 | 25 | - (void)setupUI; 26 | - (void)setupTweens; 27 | - (void)startTouchedHandler:(id)sender; 28 | - (void)stopTouchedHandler:(id)sender; 29 | - (void)pauseTouchedHandler:(id)sender; 30 | - (void)resumeTouchedHandler:(id)sender; 31 | 32 | @end 33 | 34 | @implementation SequenceNoncontiguousVC 35 | 36 | - (void)viewDidLoad { 37 | [super viewDidLoad]; 38 | 39 | self.view.backgroundColor = [UIColor whiteColor]; 40 | } 41 | 42 | - (void)viewWillAppear:(BOOL)animated { 43 | [super viewWillAppear:animated]; 44 | 45 | if (!self.createdUI) { 46 | [self setupUI]; 47 | } 48 | } 49 | 50 | - (void)viewWillDisappear:(BOOL)animated { 51 | [super viewWillDisappear:animated]; 52 | 53 | [self.sequence stopTween]; 54 | } 55 | 56 | - (void)viewDidLayoutSubviews { 57 | 58 | if (!self.createdUI) { 59 | 60 | CGFloat content_top = 0; 61 | if ([self respondsToSelector:@selector(topLayoutGuide)]) { 62 | content_top = self.topLayoutGuide.length; 63 | } 64 | CGFloat currx = 70; 65 | CGFloat circle_spacer = 20; 66 | self.view1.frame = CGRectMake(currx, content_top+30, 30, 30); 67 | self.view1.layer.cornerRadius = self.view1.frame.size.width/2; 68 | self.view1.layer.masksToBounds = YES; 69 | 70 | currx += self.view1.frame.size.width + circle_spacer; 71 | self.view2.frame = CGRectMake(currx, content_top+30, 30, 30); 72 | self.view2.layer.cornerRadius = self.view2.frame.size.width/2; 73 | self.view2.layer.masksToBounds = YES; 74 | 75 | currx += self.view2.frame.size.width + circle_spacer; 76 | self.view3.frame = CGRectMake(currx, content_top+30, 30, 30); 77 | self.view3.layer.cornerRadius = self.view3.frame.size.width/2; 78 | self.view3.layer.masksToBounds = YES; 79 | 80 | currx += self.view3.frame.size.width + circle_spacer; 81 | self.view4.frame = CGRectMake(currx, content_top+30, 30, 30); 82 | self.view4.layer.cornerRadius = self.view4.frame.size.width/2; 83 | self.view4.layer.masksToBounds = YES; 84 | 85 | [self setupTweens]; 86 | 87 | self.createdUI = YES; 88 | 89 | } 90 | } 91 | 92 | - (void)setupUI { 93 | 94 | self.view1 = [[UIView alloc] init]; 95 | self.view1.backgroundColor = [UIColor colorWithRed:76.0/255.0 green:164.0/255.0 blue:68.0/255.0 alpha:1.0]; 96 | [self.view addSubview:self.view1]; 97 | 98 | self.view2 = [[UIView alloc] init]; 99 | self.view2.backgroundColor = [UIColor colorWithRed:91.0/255.0 green:189.0/255.0 blue:231.0/255.0 alpha:1.0]; 100 | [self.view addSubview:self.view2]; 101 | 102 | self.view3 = [[UIView alloc] init]; 103 | self.view3.backgroundColor = [UIColor colorWithRed:237.0/255.0 green:166.0/255.0 blue:183.0/255.0 alpha:1.0]; 104 | [self.view addSubview:self.view3]; 105 | 106 | self.view4 = [[UIView alloc] init]; 107 | self.view4.backgroundColor = [UIColor colorWithRed:216.0/255.0 green:184.0/255.0 blue:141.0/255.0 alpha:1.0]; 108 | [self.view addSubview:self.view4]; 109 | 110 | CGFloat currx = 10; 111 | CGFloat btn_y = self.view.frame.size.height - 60; 112 | CGFloat btn_spacer = 10; 113 | 114 | UIButton *start_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 115 | start_btn.frame = CGRectMake(currx, btn_y, 60, 40); 116 | [start_btn setTitle:@"Start" forState:UIControlStateNormal]; 117 | [start_btn addTarget:self action:@selector(startTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 118 | [self.view addSubview:start_btn]; 119 | 120 | currx += start_btn.frame.size.width + btn_spacer; 121 | 122 | UIButton *stop_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 123 | stop_btn.frame = CGRectMake(currx, btn_y, 60, 40); 124 | [stop_btn setTitle:@"Stop" forState:UIControlStateNormal]; 125 | [stop_btn addTarget:self action:@selector(stopTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 126 | [self.view addSubview:stop_btn]; 127 | 128 | currx += stop_btn.frame.size.width + btn_spacer; 129 | 130 | UIButton *pause_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 131 | pause_btn.frame = CGRectMake(currx, btn_y, 60, 40); 132 | [pause_btn setTitle:@"Pause" forState:UIControlStateNormal]; 133 | [pause_btn addTarget:self action:@selector(pauseTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 134 | [self.view addSubview:pause_btn]; 135 | 136 | currx += pause_btn.frame.size.width + btn_spacer; 137 | 138 | UIButton *resume_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 139 | resume_btn.frame = CGRectMake(currx, btn_y, 60, 40); 140 | [resume_btn setTitle:@"Resume" forState:UIControlStateNormal]; 141 | [resume_btn addTarget:self action:@selector(resumeTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 142 | [self.view addSubview:resume_btn]; 143 | } 144 | 145 | 146 | 147 | - (void)setupTweens { 148 | 149 | PMTweenEasingBlock easing_cubic = [PMTweenEasingCubic easingInOut]; 150 | 151 | PMTweenUnit *tween1 = [[PMTweenUnit alloc] initWithObject:self.view1 propertyKeyPath:@"layer.bounds.size" startingValue:30 endingValue:50 duration:0.8 options:PMTweenOptionReverse easingBlock:easing_cubic]; 152 | PMTweenUnit *tween2 = [[PMTweenUnit alloc] initWithObject:self.view2 propertyKeyPath:@"layer.bounds.size" startingValue:30 endingValue:50 duration:0.8 options:PMTweenOptionReverse easingBlock:easing_cubic]; 153 | PMTweenUnit *tween3 = [[PMTweenUnit alloc] initWithObject:self.view3 propertyKeyPath:@"layer.bounds.size" startingValue:30 endingValue:50 duration:0.8 options:PMTweenOptionReverse easingBlock:easing_cubic]; 154 | PMTweenUnit *tween4 = [[PMTweenUnit alloc] initWithObject:self.view4 propertyKeyPath:@"layer.bounds.size" startingValue:30 endingValue:50 duration:0.8 options:PMTweenOptionReverse easingBlock:easing_cubic]; 155 | 156 | 157 | self.sequence = [[PMTweenSequence alloc] initWithSequenceSteps:@[tween1, tween2, tween3, tween4] options:PMTweenOptionReverse]; 158 | self.sequence.reversingMode = PMTweenSequenceReversingNoncontiguous; 159 | 160 | self.sequence.updateBlock = ^void(NSObject *tween) { 161 | PMTweenSequence *seq = (PMTweenSequence *)tween; 162 | PMTweenUnit *unit = (PMTweenUnit *)[seq currentSequenceStep]; 163 | UIView *tween_target = (UIView *)unit.targetObject; 164 | tween_target.layer.cornerRadius = unit.currentValue/2; 165 | 166 | }; 167 | } 168 | 169 | 170 | - (void)startTouchedHandler:(id)sender { 171 | [self.sequence startTween]; 172 | } 173 | 174 | - (void)stopTouchedHandler:(id)sender { 175 | [self.sequence stopTween]; 176 | } 177 | 178 | - (void)pauseTouchedHandler:(id)sender { 179 | [self.sequence pauseTween]; 180 | } 181 | 182 | - (void)resumeTouchedHandler:(id)sender { 183 | [self.sequence resumeTween]; 184 | } 185 | 186 | @end 187 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/SequenceTweenVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // SequenceTweenVC.h 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 4/22/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SequenceTweenVC : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/SequenceTweenVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // SequenceTweenVC.m 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 4/22/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "SequenceTweenVC.h" 10 | #import "PMTweenCATempo.h" 11 | #import "PMTweenEasingCubic.h" 12 | #import "PMTweenEasingCircular.h" 13 | #import "PMTweenUnit.h" 14 | #import "PMTweenGroup.h" 15 | #import "PMTweenSequence.h" 16 | 17 | @interface SequenceTweenVC () 18 | 19 | @property (nonatomic, assign) BOOL createdUI; 20 | @property (nonatomic, strong) UIView *tweenView; 21 | @property (nonatomic, strong) PMTweenSequence *sequence; 22 | 23 | - (void)setupUI; 24 | - (void)setupTweens; 25 | - (void)startTouchedHandler:(id)sender; 26 | - (void)stopTouchedHandler:(id)sender; 27 | - (void)pauseTouchedHandler:(id)sender; 28 | - (void)resumeTouchedHandler:(id)sender; 29 | 30 | @end 31 | 32 | @implementation SequenceTweenVC 33 | 34 | - (void)viewDidLoad { 35 | [super viewDidLoad]; 36 | 37 | self.view.backgroundColor = [UIColor whiteColor]; 38 | } 39 | 40 | - (void)viewWillAppear:(BOOL)animated { 41 | [super viewWillAppear:animated]; 42 | 43 | if (!self.createdUI) { 44 | [self setupUI]; 45 | } 46 | } 47 | 48 | - (void)viewWillDisappear:(BOOL)animated { 49 | [super viewWillDisappear:animated]; 50 | 51 | [self.sequence stopTween]; 52 | } 53 | 54 | - (void)viewDidLayoutSubviews { 55 | 56 | if (!self.createdUI) { 57 | 58 | CGFloat content_top = 0; 59 | if ([self respondsToSelector:@selector(topLayoutGuide)]) { 60 | content_top = self.topLayoutGuide.length; 61 | } 62 | self.tweenView.frame = CGRectMake(20, content_top+20, 50, 50); 63 | 64 | [self setupTweens]; 65 | 66 | self.createdUI = YES; 67 | } 68 | } 69 | 70 | - (void)setupUI { 71 | 72 | self.tweenView = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 50, 50)]; 73 | self.tweenView.backgroundColor = [UIColor redColor]; 74 | [self.view addSubview:self.tweenView]; 75 | 76 | CGFloat currx = 10; 77 | CGFloat btn_y = self.view.frame.size.height - 60; 78 | CGFloat btn_spacer = 10; 79 | 80 | UIButton *start_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 81 | start_btn.frame = CGRectMake(currx, btn_y, 60, 40); 82 | [start_btn setTitle:@"Start" forState:UIControlStateNormal]; 83 | [start_btn addTarget:self action:@selector(startTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 84 | [self.view addSubview:start_btn]; 85 | 86 | currx += start_btn.frame.size.width + btn_spacer; 87 | 88 | UIButton *stop_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 89 | stop_btn.frame = CGRectMake(currx, btn_y, 60, 40); 90 | [stop_btn setTitle:@"Stop" forState:UIControlStateNormal]; 91 | [stop_btn addTarget:self action:@selector(stopTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 92 | [self.view addSubview:stop_btn]; 93 | 94 | currx += stop_btn.frame.size.width + btn_spacer; 95 | 96 | UIButton *pause_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 97 | pause_btn.frame = CGRectMake(currx, btn_y, 60, 40); 98 | [pause_btn setTitle:@"Pause" forState:UIControlStateNormal]; 99 | [pause_btn addTarget:self action:@selector(pauseTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 100 | [self.view addSubview:pause_btn]; 101 | 102 | currx += pause_btn.frame.size.width + btn_spacer; 103 | 104 | UIButton *resume_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 105 | resume_btn.frame = CGRectMake(currx, btn_y, 60, 40); 106 | [resume_btn setTitle:@"Resume" forState:UIControlStateNormal]; 107 | [resume_btn addTarget:self action:@selector(resumeTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 108 | [self.view addSubview:resume_btn]; 109 | } 110 | 111 | 112 | - (void)setupTweens { 113 | PMTweenEasingBlock easing_cubic = [PMTweenEasingCubic easingInOut]; 114 | PMTweenEasingBlock easing_circular = [PMTweenEasingCircular easingInOut]; 115 | 116 | PMTweenUnit *tween = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.origin.x" startingValue:self.tweenView.frame.origin.x endingValue:110 duration:1.0 options:PMTweenOptionNone easingBlock:easing_cubic]; 117 | PMTweenUnit *tween2 = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.origin.y" startingValue:self.tweenView.frame.origin.y endingValue:250 duration:1.25 options:PMTweenOptionNone easingBlock:easing_cubic]; 118 | PMTweenUnit *tween3 = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"backgroundColor.blue" startingValue:0.0 endingValue:1 duration:0.85 options:PMTweenOptionNone easingBlock:easing_cubic]; 119 | PMTweenUnit *tween4 = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.size.width" startingValue:self.tweenView.frame.size.width endingValue:200 duration:1.0 options:PMTweenOptionNone easingBlock:easing_circular]; 120 | 121 | PMTweenGroup *group = [[PMTweenGroup alloc] initWithTweens:@[tween, tween3] options:PMTweenOptionNone]; 122 | 123 | self.sequence = [[PMTweenSequence alloc] initWithSequenceSteps:@[group, tween2, tween4] options:PMTweenOptionReverse]; 124 | self.sequence.reversingMode = PMTweenSequenceReversingContiguous; 125 | self.sequence.stepBlock = ^void(NSObject *tween) { 126 | NSLog(@"STEP COMPLETE!"); 127 | }; 128 | self.sequence.completeBlock = ^void(NSObject *tween) { 129 | NSLog(@"COMPLETE!"); 130 | }; 131 | } 132 | 133 | 134 | - (void)startTouchedHandler:(id)sender { 135 | [self.sequence startTween]; 136 | } 137 | 138 | - (void)stopTouchedHandler:(id)sender { 139 | [self.sequence stopTween]; 140 | } 141 | 142 | - (void)pauseTouchedHandler:(id)sender { 143 | [self.sequence pauseTween]; 144 | } 145 | 146 | - (void)resumeTouchedHandler:(id)sender { 147 | [self.sequence resumeTween]; 148 | } 149 | 150 | @end 151 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/TableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewController.h 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 4/22/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TableViewController : UIViewController 12 | 13 | typedef NS_ENUM(NSInteger, PMTweenExampleType) { 14 | PMTweenExampleBasic = 0, 15 | PMTweenExampleAdditive = 1, 16 | PMTweenExampleBasicPhysics = 2, 17 | PMTweenExampleGroup = 3, 18 | PMTweenExampleSequence = 4, 19 | PMTweenExampleNoncontiguousSequence = 5, 20 | PMTweenExampleTransform3D = 6, 21 | PMTweenExampleDynamic = 7, 22 | PMTweenExampleMassTweens = 8 23 | }; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/TableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewController.m 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 4/22/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "TableViewController.h" 10 | #import "BasicTweenVC.h" 11 | #import "AdditiveVC.h" 12 | #import "BasicPhysicsTweenVC.h" 13 | #import "GroupTweenVC.h" 14 | #import "SequenceTweenVC.h" 15 | #import "SequenceNoncontiguousVC.h" 16 | #import "Transform3DVC.h" 17 | #import "DynamicTweenVC.h" 18 | #import "MassTweensVC.h" 19 | 20 | @interface TableViewController () 21 | 22 | @property (nonatomic, strong) NSArray *examples; 23 | @property (nonatomic, strong) UITableView *tableView; 24 | @property (nonatomic, assign) BOOL uiCreated; 25 | 26 | - (void)setupUI; 27 | 28 | @end 29 | 30 | @implementation TableViewController 31 | 32 | - (instancetype)init { 33 | self = [super init]; 34 | if (self) { 35 | // Custom initialization 36 | 37 | _examples = @[@"Basic Tween", 38 | @"Additive Tween", 39 | @"Basic Physics Tween", 40 | @"Group (+Reversing)", 41 | @"Sequence (Rev. Contiguous)", 42 | @"Sequence (Rev. Noncontiguous)", 43 | @"CATransform3D Tween", 44 | @"Dynamic Additive Tween", 45 | @"250 Random Tweens" 46 | ]; 47 | 48 | } 49 | return self; 50 | } 51 | 52 | - (void)viewDidLoad { 53 | [super viewDidLoad]; 54 | 55 | self.title = @"Examples"; 56 | self.view.backgroundColor = [UIColor whiteColor]; 57 | } 58 | 59 | - (void)viewWillLayoutSubviews { 60 | [super viewWillLayoutSubviews]; 61 | 62 | if (!self.uiCreated) { 63 | [self setupUI]; 64 | } 65 | 66 | } 67 | 68 | - (void)setupUI { 69 | self.tableView = [[UITableView alloc] init]; 70 | self.tableView.dataSource = self; 71 | self.tableView.delegate = self; 72 | self.tableView.clipsToBounds = YES; 73 | self.tableView.estimatedRowHeight = 44.0; 74 | [self.view addSubview:self.tableView]; 75 | 76 | self.tableView.translatesAutoresizingMaskIntoConstraints = NO; 77 | 78 | [NSLayoutConstraint constraintWithItem:self.tableView 79 | attribute:NSLayoutAttributeWidth 80 | relatedBy:NSLayoutRelationEqual 81 | toItem:self.view 82 | attribute:NSLayoutAttributeWidth 83 | multiplier:1.0 84 | constant:0.0].active = YES; 85 | 86 | [NSLayoutConstraint constraintWithItem:self.tableView 87 | attribute:NSLayoutAttributeHeight 88 | relatedBy:NSLayoutRelationEqual 89 | toItem:self.view 90 | attribute:NSLayoutAttributeHeight 91 | multiplier:1.0 92 | constant:0.0].active = YES; 93 | 94 | self.uiCreated = YES; 95 | } 96 | 97 | 98 | #pragma mark - Table view data source 99 | 100 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 101 | return 1; 102 | } 103 | 104 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 105 | return (NSInteger)[self.examples count]; 106 | } 107 | 108 | 109 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 110 | 111 | NSUInteger index = (NSUInteger)[indexPath row]; 112 | static NSString *identifier = @"ExampleCell"; 113 | UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:identifier]; 114 | if (cell == nil) { 115 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; 116 | } 117 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 118 | 119 | NSString *label_text = self.examples[index]; 120 | 121 | 122 | [cell.textLabel setText:label_text]; 123 | 124 | 125 | return cell; 126 | } 127 | 128 | #pragma mark - Table view delegate 129 | 130 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 131 | 132 | NSUInteger index = (NSUInteger)[indexPath row]; 133 | 134 | UIViewController *vc = nil; 135 | switch (index) { 136 | case PMTweenExampleBasic: 137 | vc = [[BasicTweenVC alloc] init]; 138 | break; 139 | case PMTweenExampleAdditive: 140 | vc = [[AdditiveVC alloc] init]; 141 | break; 142 | case PMTweenExampleBasicPhysics: 143 | vc = [[BasicPhysicsTweenVC alloc] init]; 144 | break; 145 | case PMTweenExampleGroup: 146 | vc = [[GroupTweenVC alloc] init]; 147 | break; 148 | case PMTweenExampleSequence: 149 | vc = [[SequenceTweenVC alloc] init]; 150 | break; 151 | case PMTweenExampleNoncontiguousSequence: 152 | vc = [[SequenceNoncontiguousVC alloc] init]; 153 | break; 154 | case PMTweenExampleTransform3D: 155 | vc = [[Transform3DVC alloc] init]; 156 | break; 157 | case PMTweenExampleDynamic: 158 | vc = [[DynamicTweenVC alloc] init]; 159 | break; 160 | case PMTweenExampleMassTweens: 161 | vc = [[MassTweensVC alloc] init]; 162 | break; 163 | default: 164 | vc = [[BasicTweenVC alloc] init]; 165 | break; 166 | } 167 | 168 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 169 | 170 | vc.title = self.examples[index]; 171 | [self.navigationController pushViewController:vc animated:YES]; 172 | } 173 | 174 | 175 | 176 | @end 177 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/Transform3DVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // Transform3DVC.h 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 4/23/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface Transform3DVC : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Classes/Transform3DVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // Transform3DVC.m 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 4/23/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "Transform3DVC.h" 10 | #import "PMTweenCATempo.h" 11 | #import "PMTweenEasingLinear.h" 12 | #import "PMTweenEasingSine.h" 13 | #import "PMTweenUnit.h" 14 | #import "PMTweenGroup.h" 15 | 16 | @interface Transform3DVC () 17 | 18 | @property (nonatomic, assign) BOOL createdUI; 19 | @property (nonatomic, strong) UIView *tweenView; 20 | @property (nonatomic, strong) UIView *tweenView2; 21 | @property (nonatomic, strong) CALayer *layer1; 22 | @property (nonatomic, strong) CALayer *layer2; 23 | @property (nonatomic, strong) PMTweenGroup *group; 24 | 25 | - (void)setupUI; 26 | - (void)setupEasing; 27 | - (void)startTouchedHandler:(id)sender; 28 | - (void)stopTouchedHandler:(id)sender; 29 | - (void)pauseTouchedHandler:(id)sender; 30 | - (void)resumeTouchedHandler:(id)sender; 31 | 32 | @end 33 | 34 | @implementation Transform3DVC 35 | 36 | - (void)viewDidLoad { 37 | [super viewDidLoad]; 38 | 39 | self.view.backgroundColor = [UIColor whiteColor]; 40 | } 41 | 42 | 43 | - (void)viewWillAppear:(BOOL)animated { 44 | [super viewWillAppear:animated]; 45 | 46 | if (!self.createdUI) { 47 | [self setupUI]; 48 | } 49 | } 50 | 51 | - (void)viewWillDisappear:(BOOL)animated { 52 | [super viewWillDisappear:animated]; 53 | 54 | [self.group stopTween]; 55 | } 56 | 57 | - (void)viewDidLayoutSubviews { 58 | 59 | if (!self.createdUI) { 60 | CGFloat content_top = 0; 61 | if ([self respondsToSelector:@selector(topLayoutGuide)]) { 62 | content_top = self.topLayoutGuide.length; 63 | } 64 | self.tweenView.frame = CGRectMake(110, content_top+100, 50, 50); 65 | self.tweenView2.frame = CGRectMake(160, content_top+100, 50, 50); 66 | 67 | [self setupEasing]; 68 | 69 | self.createdUI = YES; 70 | } 71 | } 72 | 73 | - (void)setupUI { 74 | 75 | self.tweenView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)]; 76 | [self.view addSubview:self.tweenView]; 77 | self.tweenView2 = [[UIView alloc] initWithFrame:CGRectMake(150, 100, 200, 200)]; 78 | [self.view addSubview:self.tweenView2]; 79 | 80 | self.layer1 = [CALayer layer]; 81 | self.layer1.backgroundColor = [UIColor colorWithRed:76.0/255.0 green:164.0/255.0 blue:68.0/255.0 alpha:1.0].CGColor; 82 | self.layer1.frame = CGRectMake(1, 1, 99, 99); 83 | self.layer1.position = CGPointMake(0,0); 84 | [self.tweenView.layer addSublayer:self.layer1]; 85 | self.layer2 = [CALayer layer]; 86 | self.layer2.backgroundColor = [UIColor colorWithRed:91.0/255.0 green:189.0/255.0 blue:231.0/255.0 alpha:1.0].CGColor; 87 | self.layer2.frame = CGRectMake(1, 1, 99, 99); 88 | self.layer2.position = CGPointMake(51,0); 89 | [self.tweenView2.layer addSublayer:self.layer2]; 90 | 91 | CATransform3D initialTransform = self.layer1.sublayerTransform; 92 | initialTransform.m34 = 1.0 / -350; 93 | self.layer1.sublayerTransform = initialTransform; 94 | self.layer2.sublayerTransform = initialTransform; 95 | 96 | CGFloat currx = 10; 97 | CGFloat btn_y = self.view.frame.size.height - 60; 98 | CGFloat btn_spacer = 10; 99 | 100 | UIButton *start_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 101 | start_btn.frame = CGRectMake(currx, btn_y, 60, 40); 102 | [start_btn setTitle:@"Start" forState:UIControlStateNormal]; 103 | [start_btn addTarget:self action:@selector(startTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 104 | [self.view addSubview:start_btn]; 105 | 106 | currx += start_btn.frame.size.width + btn_spacer; 107 | 108 | UIButton *stop_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 109 | stop_btn.frame = CGRectMake(currx, btn_y, 60, 40); 110 | [stop_btn setTitle:@"Stop" forState:UIControlStateNormal]; 111 | [stop_btn addTarget:self action:@selector(stopTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 112 | [self.view addSubview:stop_btn]; 113 | 114 | currx += stop_btn.frame.size.width + btn_spacer; 115 | 116 | UIButton *pause_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 117 | pause_btn.frame = CGRectMake(currx, btn_y, 60, 40); 118 | [pause_btn setTitle:@"Pause" forState:UIControlStateNormal]; 119 | [pause_btn addTarget:self action:@selector(pauseTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 120 | [self.view addSubview:pause_btn]; 121 | 122 | currx += pause_btn.frame.size.width + btn_spacer; 123 | 124 | UIButton *resume_btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 125 | resume_btn.frame = CGRectMake(currx, btn_y, 60, 40); 126 | [resume_btn setTitle:@"Resume" forState:UIControlStateNormal]; 127 | [resume_btn addTarget:self action:@selector(resumeTouchedHandler:) forControlEvents:UIControlEventTouchUpInside]; 128 | [self.view addSubview:resume_btn]; 129 | 130 | } 131 | 132 | 133 | - (void)setupEasing { 134 | 135 | PMTweenEasingBlock easing = [PMTweenEasingSine easingInOut]; 136 | //self.tween = [[PMTweenUnit alloc] initWithProperty:@(0) startingValue:0 endingValue:0.01 duration:1.0 options:PMTweenOptionRepeat easingBlock:easing]; 137 | PMTweenUnit *tween1 = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"layer.sublayerTransform.m13" startingValue:self.tweenView.layer.sublayerTransform.m13 endingValue:0.0025 duration:2.0 options:PMTweenOptionNone easingBlock:easing]; 138 | PMTweenUnit *tween2 = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"layer.sublayerTransform.m14" startingValue:self.tweenView.layer.sublayerTransform.m14 endingValue:-0.004 duration:2.0 options:PMTweenOptionNone easingBlock:easing]; 139 | PMTweenUnit *tween3 = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"layer.sublayerTransform.m11" startingValue:self.tweenView.layer.sublayerTransform.m11 endingValue:-0.30 duration:2.0 options:PMTweenOptionNone easingBlock:easing]; 140 | 141 | PMTweenUnit *tween4 = [[PMTweenUnit alloc] initWithObject:self.tweenView2 propertyKeyPath:@"layer.sublayerTransform.m13" startingValue:self.tweenView.layer.sublayerTransform.m13 endingValue:0.0025 duration:2.0 options:PMTweenOptionNone easingBlock:easing]; 142 | PMTweenUnit *tween5 = [[PMTweenUnit alloc] initWithObject:self.tweenView2 propertyKeyPath:@"layer.sublayerTransform.m14" startingValue:self.tweenView.layer.sublayerTransform.m14 endingValue:-0.004 duration:2.0 options:PMTweenOptionNone easingBlock:easing]; 143 | PMTweenUnit *tween6 = [[PMTweenUnit alloc] initWithObject:self.tweenView2 propertyKeyPath:@"layer.sublayerTransform.m11" startingValue:self.tweenView.layer.sublayerTransform.m11 endingValue:-0.30 duration:2.0 options:PMTweenOptionNone easingBlock:easing]; 144 | 145 | self.group = [[PMTweenGroup alloc] initWithTweens:@[tween1, tween2, tween3, tween4, tween5, tween6] options:PMTweenOptionRepeat|PMTweenOptionReverse]; 146 | self.group.numberOfRepeats = NSIntegerMax; 147 | 148 | } 149 | 150 | 151 | - (void)startTouchedHandler:(id)sender { 152 | [self.group startTween]; 153 | } 154 | 155 | - (void)stopTouchedHandler:(id)sender { 156 | [self.group stopTween]; 157 | } 158 | 159 | - (void)pauseTouchedHandler:(id)sender { 160 | [self.group pauseTween]; 161 | } 162 | 163 | - (void)resumeTouchedHandler:(id)sender { 164 | [self.group resumeTween]; 165 | } 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "8.0", 8 | "subtype" : "736h", 9 | "scale" : "3x" 10 | }, 11 | { 12 | "orientation" : "landscape", 13 | "idiom" : "iphone", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "8.0", 16 | "subtype" : "736h", 17 | "scale" : "3x" 18 | }, 19 | { 20 | "orientation" : "portrait", 21 | "idiom" : "iphone", 22 | "extent" : "full-screen", 23 | "minimum-system-version" : "8.0", 24 | "subtype" : "667h", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "orientation" : "portrait", 29 | "idiom" : "iphone", 30 | "extent" : "full-screen", 31 | "minimum-system-version" : "7.0", 32 | "scale" : "2x" 33 | }, 34 | { 35 | "orientation" : "portrait", 36 | "idiom" : "iphone", 37 | "extent" : "full-screen", 38 | "minimum-system-version" : "7.0", 39 | "subtype" : "retina4", 40 | "scale" : "2x" 41 | }, 42 | { 43 | "orientation" : "portrait", 44 | "idiom" : "iphone", 45 | "extent" : "full-screen", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "orientation" : "portrait", 50 | "idiom" : "iphone", 51 | "extent" : "full-screen", 52 | "scale" : "2x" 53 | }, 54 | { 55 | "orientation" : "portrait", 56 | "idiom" : "iphone", 57 | "extent" : "full-screen", 58 | "subtype" : "retina4", 59 | "scale" : "2x" 60 | } 61 | ], 62 | "info" : { 63 | "version" : 1, 64 | "author" : "xcode" 65 | } 66 | } -------------------------------------------------------------------------------- /Examples/PMTweenExamples/Launch Screen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/PMTweenExamples-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 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.0 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | Launch Screen 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarTintParameters 34 | 35 | UINavigationBar 36 | 37 | Style 38 | UIBarStyleDefault 39 | Translucent 40 | 41 | 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/PMTweenExamples-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 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Examples/PMTweenExamples/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // PMTweenExamples 4 | // 5 | // Created by Brett Walker on 4/22/14. 6 | // Copyright (c) 2014 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ## License 2 | ### MIT License 3 | 4 | Copyright (c) 2014 Poet & Mountain, LLC (http://poetmountain.com) 5 | 6 | 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: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | 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. -------------------------------------------------------------------------------- /PMTween.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'PMTween' 3 | s.version = '1.3.7' 4 | s.license = { :type => 'MIT' } 5 | s.summary = 'An elegant and flexible tweening library for iOS.' 6 | s.ios.deployment_target = '7.0' 7 | s.tvos.deployment_target = '9.0' 8 | s.ios.frameworks = 'CoreGraphics' 9 | s.tvos.frameworks = 'CoreGraphics' 10 | s.homepage = 'https://github.com/poetmountain/PMTween' 11 | s.social_media_url = 'https://twitter.com/petsound' 12 | s.authors = { 'Brett Walker' => 'brett@brettwalker.net' } 13 | s.source = { :git => 'https://github.com/poetmountain/PMTween.git', :tag => "#{s.version}" } 14 | s.ios.source_files = 'Classes/**/*.{h,m}' 15 | s.tvos.source_files = 'Classes/**/*.{h,m}' 16 | s.requires_arc = true 17 | end 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PMTween is an elegant and flexible tweening library for Objective-C, currently supporting the iOS and tvOS platforms. It offers sensible default functionality that abstracts most of the hard work away, allowing you to focus on your animations and other tween tasks. But PMTween also makes it easy to dive in and modify for your own needs, whether that be custom tweening classes, supporting custom object types, or new easing equations. 2 | 3 | If you're coding in Swift, try [MotionMachine](https://github.com/poetmountain/MotionMachine), which is based on PMTween, but with many improvements and an API tailored to that langauge. 4 | 5 | ## Overview 6 | 7 | * PMTween makes both simple and complex tweening tasks easy to create. 8 | * Tweens can be grouped, sequenced, and nested in most any configuration you might need. 9 | * Includes both static and physics-based tween classes. 10 | * PMTween was built to easily support custom value types, easing equations, and functionality. 11 | * Provides notifications and blocks for many kinds of status events. 12 | 13 | All of the base tweening classes in PMTween adopt the PMTweening protocol, and you can add any of these classes to a PMTweenGroup or PMTweenSequence instance. They can be nested as many levels deep as you'd like, and PMTweenGroup and PMTweenSequence respect the tweening properties of their children. If you want to use your own custom tween classes, simply adopt the protocol. However, PMTweenUnit offers such modularity that in most cases you can just replace the parts you need with your own implementation. 14 | 15 | **In other words, this code:** 16 | 17 | ``` objc 18 | PMTweenEasingBlock easing = [PMTweenEasingCubic easingInOut]; 19 | PMTweenEasingBlock easing2 = [PMTweenEasingCircular easingInOut]; 20 | 21 | PMTweenUnit *tween1 = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.origin.x" startingValue:self.tweenView.frame.origin.x endingValue:110 duration:1.5 options:PMTweenOptionNone easingBlock:easing]; 22 | PMTweenUnit *tween2 = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"backgroundColor.blue" startingValue:0.30 endingValue:1.0 duration:1.2 options:PMTweenOptionNone easingBlock:easing]; 23 | PMTweenUnit *tween3 = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.size.width" startingValue:self.tweenView.frame.size.width endingValue:120 duration:0.9 options:PMTweenOptionNone easingBlock:easing2]; 24 | PMTweenUnit *tween4 = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.origin.y" startingValue:self.tweenView.frame.origin.y endingValue:100 duration:1.0 options:PMTweenOptionNone easingBlock:easing]; 25 | 26 | PMTweenGroup *group = [[PMTweenGroup alloc] initWithTweens:@[tween1, tween2] options:PMTweenOptionNone]; 27 | 28 | PMTweenSequence *sequence = [[PMTweenSequence alloc] initWithSequenceSteps:@[group, tween3, tween4] options:PMTweenOptionReverse|PMTweenOptionRepeat]; 29 | sequence.reversingMode = PMTweenSequenceReversingContiguous; 30 | [sequence startTween]; 31 | ``` 32 | 33 | **produces this animation:** 34 | 35 | ![sequence](http://poetmountain.github.io/PMTween/screenshots/sequence.gif) 36 | 37 | 38 | ### New in 1.2.0 39 | 40 | New in the 1.2.0 release is support for additive animations in PMTweenUnit. Additive animation allows multiple tweens to produce a compound effect, instead of overwriting each other as they update the same property. Additive animation is now the default behavior for tweening animations in iOS 8, and is great for making user interface animations fluid and responsive. Of course with PMTweenUnit you are not limited to tweening UIView properties, so additive tweening may be applied to any compatible property. Simply set `additive` to YES on any PMTweenUnit instances whose tweening updates you wish to composite. Please see the documentation and updated [Examples project](https://github.com/poetmountain/PMTween/tree/master/Examples) for more information and things you should keep in mind when using this mode. Special thanks to [Andy Matuschak](https://twitter.com/andy_matuschak) and [Kevin Doughty](https://twitter.com/DoughtyKevin) for making the concept clearer. 41 | 42 | ![additive](http://poetmountain.github.io/PMTween/screenshots/additive.gif) 43 | 44 | 45 | ### New in 1.1.0 46 | 47 | New in the 1.1.0 release is PMTweenPhysicsUnit, which provides dynamic, physics-based tweening. (Thanks to [Pop](https://github.com/facebook/pop) lib for the inspiration!) Since it adopts the PMTweening protocol, you can combine it with the other PMTween classes as you see fit. 48 | 49 | For more information, see the included [examples](https://github.com/poetmountain/PMTween/tree/master/Examples) of PMTween usage, and peruse the [documentation](https://poetmountain.github.io/PMTween/). 50 | 51 | 52 | ## Getting Started 53 | 54 | ### Installation 55 | 56 | If you use CocoaPods: 57 | 58 | ##### Podfile 59 | ```ruby 60 | pod "PMTween", "~> 1.3.0" 61 | ``` 62 | 63 | Or add the Classes directory to your project. 64 | 65 | 66 | ### Examples 67 | 68 | #### Basic tween 69 | 70 | Here's the most basic type of tween, which just tweens an arbitrary data structure directly. PMTweenUnit is the workhorse tweening class in PMTween; it handles all the actual property updating. Note that nil is passed in for the easingBlock, which means it will use the default PMTweenEasingLinear easing type. 71 | 72 | ```objc 73 | #import 74 | 75 | PMTweenUnit *tween = [[PMTweenUnit alloc] initWithProperty:@(0) startingValue:0 endingValue:100 duration:1.0 options:PMTweenOptionNone easingBlock:nil]; 76 | [tween startTween]; 77 | ``` 78 | 79 | Here's another basic example. Notice that this time we've passed an options bitmask into the options parameter which will make it tween in reverse back to the starting value (after hitting the endingValue), and also repeat its tween for multiple cycles. In this case, it would repeat the total tween operation (forwards and back). 80 | 81 | We've also defined a completeBlock, which will be called when the tween has completed all repeat cycles. Notice that the block's parameter specifies `NSObject`, and not PMTweenUnit. It denotes that this could be any object that conforms to the PMTweening protocol. If you want to access specific properties from your tweening class that are not specified by the PMTweening protocol, you will need to cast the 'tween' object to your specific class type first. 82 | 83 | ```objc 84 | PMTweenUnit *tween = [[PMTweenUnit alloc] initWithProperty:@(0) startingValue:0 endingValue:100 duration:1.0 options:PMTweenOptionRepeat|PMTweenOptionReverse easingBlock:nil]; 85 | tween.completeBlock = ^void(NSObject *tween) { 86 | NSLog(@"tween complete!"); 87 | }; 88 | tween.numberOfRepeats = 2; 89 | [tween startTween]; 90 | ``` 91 | 92 | #### Tweening an object's property 93 | 94 | A more common need is to tween an object property, such as the x position of a UIView. Check the example below. Notice that this method requires a key path. This is the object hierarchy from the object down to the property you want to tween. Even though the 'x' value can't be set directly on a frame's origin, PMTween is smart enough to handle this updating for you. PMTween handles most common UIKit properties; see the docs or the code for exactly what it supports, and let me know what it's missing. 95 | 96 | Notice also that we've defined an easingBlock this time. You can also assign a different easing block to use when reversing a tween by setting a PMTweenUnit's reverseEasingBlock property. PMTween includes all the standard Robert Penner easing types, but you can also easily use your own easing classes. Maybe you want to modify the easing by applying a Perlin noise filter or shifting the value with gyroscope data. Who knows? I don't. That's why PMTween makes it easy for you to do your own thing. 97 | 98 | ```objc 99 | PMTweenEasingBlock easing = [PMTweenEasingCubic easingInOut]; 100 | PMTweenUnit *tween = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.origin.x" startingValue:self.tweenView.frame.origin.x endingValue:200 duration:1.0 options:PMTweenOptionNone easingBlock:easing]; 101 | [tween startTween]; 102 | ``` 103 | 104 | #### Using a PMTweenGroup 105 | 106 | PMTweenGroup manages multiple class instances. It's handy for controlling and synchronizing multiple tween objects. You can of course have groups within other groups. 107 | 108 | Notice in this example that a tempo object is being set on the PMTweenGroup. It provides a tempo, or in other words a rate at which the tween should update its easing calculations. This is not necessary to set unless you want to use your own custom tempo object – all PMTween tweening classes by default create their own tempo objects internally. 109 | 110 | ```objc 111 | PMTweenEasingBlock easing = [PMTweenEasingCubic easingInOut]; 112 | PMTweenUnit *tween = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.size.width" startingValue:self.tweenView.frame.size.width endingValue:200 duration:1.0 options:PMTweenOptionNone easingBlock:easing]; 113 | PMTweenUnit *tween2 = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.size.height" startingValue:self.tweenView.frame.size.height endingValue:300 duration:2.0 options:PMTweenOptionNone easingBlock:easing]; 114 | 115 | PMTweenGroup *group = [[PMTweenGroup alloc] initWithTweens:@[tween, tween2] options:PMTweenOptionReverse]; 116 | [group setTempo:[PMTweenCATempo tempo]]; 117 | [group startTween]; 118 | ``` 119 | 120 | #### Using a PMTweenSequence 121 | 122 | PMTweenSequence plays a collection of tweens in order. Chaining tweens together is extremely easy. This is a powerful class, as you can play in sequence any assortment of PMTweenUnits, PMTweenGroups, or other PMTweenSequence classes, as well as your own custom tween classes. It's tweens all the way down. Note that we pass in an array of sequence steps – it will play the sequence in that order. 123 | 124 | When a PMTweenSequence is set to reverse, by default it will play as discrete elements when reversing (in other words, each child tween would just play forwards, but in reverse order). But by setting the sequence's reversingMode to PMTweenSequenceReversingContiguous you can set the whole sequence to play in reverse seamlessly, as if it was one contiguous tween. This is really great for complex animations. 125 | 126 | ```objc 127 | PMTweenEasingBlock easing = [PMTweenEasingCubic easingInOut]; 128 | PMTweenUnit *tween = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.origin.x" startingValue:self.tweenView.frame.origin.x endingValue:200 duration:1.0 options:PMTweenOptionNone easingBlock:easing]; 129 | PMTweenUnit *tween2 = [[PMTweenUnit alloc] initWithObject:self.tweenView propertyKeyPath:@"frame.size.height" startingValue:self.tweenView.frame.size.height endingValue:300 duration:2.0 options:PMTweenOptionNone easingBlock:easing]; 130 | 131 | PMTweenSequence *sequence = [[PMTweenSequence alloc] initWithSequenceSteps:@[tween1, tween2] options:PMTweenOptionReverse]; 132 | [sequence startTween]; 133 | ``` 134 | 135 | 136 | ## Class reference 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 |
Tween Classes
PMTweenUnitPMTweenUnit handles a single tween operation on an NSValue, interpolating between specified starting and ending values.
PMTweenPhysicsUnitPMTweenPhysicsUnit handles a single physics-based tween operation on an NSValue, using a physics system to update a value with decaying velocity.
PMTweenGroupPMTweenGroup handles the tweening of one or more objects which conform to the PMTweening protocol, either being instances of PMTweenUnit or other custom classes. The PMTweenGroup class is a good solution when you want to easily synchronize the operation of many tweens.
PMTweenSequencePMTweenSequence allows objects which conform to the PMTweening protocol to be chained together in a sequential series of tween steps.
Tempo Classes
PMTweenCATempoPMTweenCATempo is a concrete subclass of PMTweenTempo, and uses a CADisplayLink object to send out tempo updates that are synchronized with the refresh rate of the display.
PMTweenBeatPMTweenBeat broadcasts updates from a PMTweenTempo object to multiple objects which adopt the PMTweening protocol. This allows you to use a single tempo object for all tween objects, avoiding a performance impact when tweening many objects.
Easing Types
PMTweenEasingLinearProvides a linear easing equation.
PMTweenEasingQuadraticProvides quadratic easing equations.
PMTweenEasingQuarticProvides quartic easing equations.
PMTweenEasingQuinticProvides quintic easing equations.
PMTweenEasingCubicProvides cubic easing equations.
PMTweenEasingSineProvides easing equations based on sine.
PMTweenEasingCircularProvides circular easing equations.
PMTweenEasingElasticProvides easing equations that behave in an elastic fashion.
PMTweenEasingExpoProvides exponential easing equations.
PMTweenEasingBackProvides back easing equations.
PMTweenEasingBounceProvides easing equations that have successively smaller value peaks, like a bouncing ball.
214 | 215 | 216 | ## Tests 217 | 218 | PMTween is tested using [Specta](https://github.com/specta/specta)/[Expecta](https://github.com/specta/expecta/). You can install the test dependencies using CocoaPods by running 'pod install' from within the Tests directory. 219 | 220 | ## Credits 221 | 222 | PMTween was created by [Brett Walker](https://twitter.com/petsound) of [Poet & Mountain](http://poetmountain.com). 223 | 224 | ## Compatibility 225 | 226 | * Requires iOS 7.0 or later, tvOS 9.0 or later 227 | * PMTween uses ARC 228 | 229 | ## License 230 | 231 | PMTween is licensed under the MIT License. I'd love to know if you use PMTween in your app! 232 | -------------------------------------------------------------------------------- /Tests/.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X Finder and whatnot 2 | .DS_Store 3 | 4 | # rvm files 5 | .ruby-version 6 | .ruby-gemset 7 | Gemfile 8 | Gemfile.lock 9 | 10 | # Xcode 11 | build/ 12 | *.pbxuser 13 | !default.pbxuser 14 | *.mode1v3 15 | !default.mode1v3 16 | *.mode2v3 17 | !default.mode2v3 18 | *.perspectivev3 19 | !default.perspectivev3 20 | *.xcworkspace 21 | !default.xcworkspace 22 | xcuserdata 23 | profile 24 | *.moved-aside 25 | DerivedData 26 | .idea 27 | 28 | # cocoapods 29 | Pods/ 30 | Podfile.lock 31 | -------------------------------------------------------------------------------- /Tests/PMTweenTests/Classes/TestObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestObject.h 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 6/2/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TestObject : NSObject 12 | 13 | @property (nonatomic, strong) NSNumber *testProp; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Tests/PMTweenTests/Classes/TestObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestObject.m 3 | // PMTween 4 | // 5 | // Created by Brett Walker on 6/2/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "TestObject.h" 10 | 11 | @implementation TestObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Tests/PMTweenTests/PMTweenTests-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 | -------------------------------------------------------------------------------- /Tests/PMTweenTests/PMTweenTests-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 | 12 | #ifdef DEBUG 13 | # define DLog(__FORMAT__, ...) NSLog((@"%s [Line %d] " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) 14 | #else 15 | # define DLog(...) do {} while (0) 16 | #endif -------------------------------------------------------------------------------- /Tests/PMTweenTests/Specs/PMTweenBeatSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenBeatSpec.m 3 | // PMTweenTests 4 | // 5 | // Created by Brett Walker on 4/8/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "Specta.h" 10 | #define EXP_SHORTHAND 11 | #import "Expecta.h" 12 | 13 | #import "PMTweenBeat.h" 14 | #import "PMTweenCATempo.h" 15 | #import "PMTweenUnit.h" 16 | 17 | SpecBegin(PMTweenBeat) 18 | 19 | describe(@"PMTweenBeat", ^{ 20 | __block PMTweenBeat *beat; 21 | 22 | describe(@"- initWithTempo:", ^{ 23 | before(^{ 24 | beat = [[PMTweenBeat alloc] initWithTempo:[PMTweenCATempo tempo]]; 25 | }); 26 | 27 | it(@"should return an instance of PMTweenBeat", ^{ 28 | expect(beat).to.beInstanceOf([PMTweenBeat class]); 29 | }); 30 | }); 31 | 32 | describe(@"- addTween", ^{ 33 | before(^{ 34 | beat = [[PMTweenBeat alloc] initWithTempo:[PMTweenCATempo tempo]]; 35 | PMTweenUnit *unit = [[PMTweenUnit alloc] initWithProperty:@(0) startingValue:0 endingValue:100 duration:0.2 options:PMTweenOptionNone easingBlock:nil]; 36 | [beat addTween:unit]; 37 | }); 38 | 39 | it(@"should populate tweens array", ^{ 40 | expect(beat.tweens).to.haveCountOf(1); 41 | }); 42 | 43 | }); 44 | 45 | describe(@"- removeTween", ^{ 46 | before(^{ 47 | beat = [[PMTweenBeat alloc] initWithTempo:[PMTweenCATempo tempo]]; 48 | PMTweenUnit *unit = [[PMTweenUnit alloc] initWithProperty:@(0) startingValue:0 endingValue:100 duration:0.2 options:PMTweenOptionNone easingBlock:nil]; 49 | PMTweenUnit *unit2 = [[PMTweenUnit alloc] initWithProperty:@(0) startingValue:0 endingValue:100 duration:0.2 options:PMTweenOptionNone easingBlock:nil]; 50 | [beat addTween:unit]; 51 | [beat addTween:unit2]; 52 | [beat removeTween:unit]; 53 | }); 54 | 55 | it(@"should reduce count of tweens array", ^{ 56 | expect(beat.tweens).to.haveCountOf(1); 57 | }); 58 | 59 | }); 60 | 61 | }); 62 | 63 | SpecEnd 64 | -------------------------------------------------------------------------------- /Tests/PMTweenTests/Specs/PMTweenEasingSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // PMTweenEasingSpec.m 3 | // PMTweenTests 4 | // 5 | // Created by Brett Walker on 4/13/14. 6 | // Copyright (c) 2014-2016 Poet & Mountain, LLC. All rights reserved. 7 | // 8 | 9 | #import "Specta.h" 10 | #define EXP_SHORTHAND 11 | #import "Expecta.h" 12 | 13 | #import "PMTweenEasingLinear.h" 14 | #import "PMTweenEasingQuadratic.h" 15 | #import "PMTweenEasingCubic.h" 16 | #import "PMTweenEasingQuartic.h" 17 | #import "PMTweenEasingQuintic.h" 18 | #import "PMTweenEasingSine.h" 19 | #import "PMTweenEasingCircular.h" 20 | #import "PMTweenEasingBack.h" 21 | #import "PMTweenEasingBounce.h" 22 | 23 | SpecBegin(PMTweenEasing) 24 | 25 | describe(@"PMTweenEasing types", ^{ 26 | __block double easing; 27 | 28 | describe(@"PMTweenEasingLinear", ^{ 29 | before(^{ 30 | PMTweenEasingBlock easingBlock = [PMTweenEasingLinear easingNone]; 31 | easing = easingBlock(100, 0, 100, 100); 32 | }); 33 | 34 | it(@"should return correct value", ^{ 35 | expect(easing).to.equal(100); 36 | }); 37 | }); 38 | 39 | describe(@"PMTweenEasingQuadratic", ^{ 40 | 41 | describe(@"easingIn", ^{ 42 | before(^{ 43 | PMTweenEasingBlock easingBlock = [PMTweenEasingQuadratic easingIn]; 44 | easing = easingBlock(100, 0, 100, 100); 45 | }); 46 | 47 | it(@"should return correct value", ^{ 48 | expect(easing).to.equal(100); 49 | }); 50 | }); 51 | 52 | describe(@"easingOut", ^{ 53 | before(^{ 54 | PMTweenEasingBlock easingBlock = [PMTweenEasingQuadratic easingOut]; 55 | easing = easingBlock(100, 0, 100, 100); 56 | }); 57 | 58 | it(@"should return correct value", ^{ 59 | expect(easing).to.equal(100); 60 | }); 61 | }); 62 | 63 | describe(@"easingInOut", ^{ 64 | before(^{ 65 | PMTweenEasingBlock easingBlock = [PMTweenEasingQuadratic easingInOut]; 66 | easing = easingBlock(100, 0, 100, 100); 67 | }); 68 | 69 | it(@"should return correct value", ^{ 70 | expect(easing).to.equal(100); 71 | }); 72 | }); 73 | 74 | }); 75 | 76 | 77 | describe(@"PMTweenEasingCubic", ^{ 78 | 79 | describe(@"easingIn", ^{ 80 | before(^{ 81 | PMTweenEasingBlock easingBlock = [PMTweenEasingCubic easingIn]; 82 | easing = easingBlock(100, 0, 100, 100); 83 | }); 84 | 85 | it(@"should return correct value", ^{ 86 | expect(easing).to.equal(100); 87 | }); 88 | }); 89 | 90 | describe(@"easingOut", ^{ 91 | before(^{ 92 | PMTweenEasingBlock easingBlock = [PMTweenEasingCubic easingOut]; 93 | easing = easingBlock(100, 0, 100, 100); 94 | }); 95 | 96 | it(@"should return correct value", ^{ 97 | expect(easing).to.equal(100); 98 | }); 99 | }); 100 | 101 | describe(@"easingInOut", ^{ 102 | before(^{ 103 | PMTweenEasingBlock easingBlock = [PMTweenEasingCubic easingInOut]; 104 | easing = easingBlock(100, 0, 100, 100); 105 | }); 106 | 107 | it(@"should return correct value", ^{ 108 | expect(easing).to.equal(100); 109 | }); 110 | }); 111 | 112 | }); 113 | 114 | describe(@"PMTweenEasingQuartic", ^{ 115 | 116 | describe(@"easingIn", ^{ 117 | before(^{ 118 | PMTweenEasingBlock easingBlock = [PMTweenEasingQuartic easingIn]; 119 | easing = easingBlock(100, 0, 100, 100); 120 | }); 121 | 122 | it(@"should return correct value", ^{ 123 | expect(easing).to.equal(100); 124 | }); 125 | }); 126 | 127 | describe(@"easingOut", ^{ 128 | before(^{ 129 | PMTweenEasingBlock easingBlock = [PMTweenEasingQuartic easingOut]; 130 | easing = easingBlock(100, 0, 100, 100); 131 | }); 132 | 133 | it(@"should return correct value", ^{ 134 | expect(easing).to.equal(100); 135 | }); 136 | }); 137 | 138 | describe(@"easingInOut", ^{ 139 | before(^{ 140 | PMTweenEasingBlock easingBlock = [PMTweenEasingQuartic easingInOut]; 141 | easing = easingBlock(100, 0, 100, 100); 142 | }); 143 | 144 | it(@"should return correct value", ^{ 145 | expect(easing).to.equal(100); 146 | }); 147 | }); 148 | 149 | }); 150 | 151 | 152 | describe(@"PMTweenEasingQuintic", ^{ 153 | 154 | describe(@"easingIn", ^{ 155 | before(^{ 156 | PMTweenEasingBlock easingBlock = [PMTweenEasingQuintic easingIn]; 157 | easing = easingBlock(100, 0, 100, 100); 158 | }); 159 | 160 | it(@"should return correct value", ^{ 161 | expect(easing).to.equal(100); 162 | }); 163 | }); 164 | 165 | describe(@"easingOut", ^{ 166 | before(^{ 167 | PMTweenEasingBlock easingBlock = [PMTweenEasingQuintic easingOut]; 168 | easing = easingBlock(100, 0, 100, 100); 169 | }); 170 | 171 | it(@"should return correct value", ^{ 172 | expect(easing).to.equal(100); 173 | }); 174 | }); 175 | 176 | describe(@"easingInOut", ^{ 177 | before(^{ 178 | PMTweenEasingBlock easingBlock = [PMTweenEasingQuintic easingInOut]; 179 | easing = easingBlock(100, 0, 100, 100); 180 | }); 181 | 182 | it(@"should return correct value", ^{ 183 | expect(easing).to.equal(100); 184 | }); 185 | }); 186 | 187 | }); 188 | 189 | 190 | describe(@"PMTweenEasingSine", ^{ 191 | 192 | describe(@"easingIn", ^{ 193 | before(^{ 194 | PMTweenEasingBlock easingBlock = [PMTweenEasingSine easingIn]; 195 | easing = easingBlock(100, 0, 100, 100); 196 | }); 197 | 198 | it(@"should return correct value", ^{ 199 | expect(easing).to.equal(100); 200 | }); 201 | }); 202 | 203 | describe(@"easingOut", ^{ 204 | before(^{ 205 | PMTweenEasingBlock easingBlock = [PMTweenEasingSine easingOut]; 206 | easing = easingBlock(100, 0, 100, 100); 207 | }); 208 | 209 | it(@"should return correct value", ^{ 210 | expect(easing).to.equal(100); 211 | }); 212 | }); 213 | 214 | describe(@"easingInOut", ^{ 215 | before(^{ 216 | PMTweenEasingBlock easingBlock = [PMTweenEasingSine easingInOut]; 217 | easing = easingBlock(100, 0, 100, 100); 218 | }); 219 | 220 | it(@"should return correct value", ^{ 221 | expect(easing).to.equal(100); 222 | }); 223 | }); 224 | 225 | }); 226 | 227 | }); 228 | 229 | 230 | 231 | SpecEnd -------------------------------------------------------------------------------- /Tests/PMTweenTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Tests/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | 3 | platform :ios, '7.0' 4 | 5 | xcodeproj 'PMTweenTests.xcodeproj' 6 | 7 | target :PMTweenTests, :exclusive => true do 8 | pod 'Specta', '~> 1.0.5' 9 | pod 'Expecta', '~> 1.0.5' 10 | end --------------------------------------------------------------------------------