├── ShootingStar
├── StarParticle.sks
├── Assets.xcassets
│ ├── Contents.json
│ ├── filled_star.imageset
│ │ ├── filled_star.png
│ │ └── Contents.json
│ ├── asterisk_filled.imageset
│ │ ├── asterisk_filled.png
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── AsteriskParticle.sks
├── ViewController.h
├── AppDelegate.h
├── main.m
├── Info.plist
├── Base.lproj
│ └── LaunchScreen.storyboard
├── AppDelegate.m
└── ViewController.m
├── ShootingStar.xcodeproj
├── xcuserdata
│ └── garchinskyr1.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── ShootingStar.xcscheme
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── project.pbxproj
└── README.md
/ShootingStar/StarParticle.sks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryang1428/ShootingStars/HEAD/ShootingStar/StarParticle.sks
--------------------------------------------------------------------------------
/ShootingStar/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/ShootingStar/AsteriskParticle.sks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryang1428/ShootingStars/HEAD/ShootingStar/AsteriskParticle.sks
--------------------------------------------------------------------------------
/ShootingStar/Assets.xcassets/filled_star.imageset/filled_star.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryang1428/ShootingStars/HEAD/ShootingStar/Assets.xcassets/filled_star.imageset/filled_star.png
--------------------------------------------------------------------------------
/ShootingStar.xcodeproj/xcuserdata/garchinskyr1.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/ShootingStar/Assets.xcassets/asterisk_filled.imageset/asterisk_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryang1428/ShootingStars/HEAD/ShootingStar/Assets.xcassets/asterisk_filled.imageset/asterisk_filled.png
--------------------------------------------------------------------------------
/ShootingStar.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ShootingStar/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // ShootingStar
4 | //
5 | // Created by Ryan Garchinsky on 12/21/15.
6 | // Copyright © 2015 garapps. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/ShootingStar/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // ShootingStar
4 | //
5 | // Created by Ryan Garchinsky on 12/21/15.
6 | // Copyright © 2015 garapps. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/ShootingStar/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // ShootingStar
4 | //
5 | // Created by Ryan Garchinsky on 12/21/15.
6 | // Copyright © 2015 garapps. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ShootingStar/Assets.xcassets/filled_star.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "filled_star.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/ShootingStar/Assets.xcassets/asterisk_filled.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "asterisk_filled.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/ShootingStar.xcodeproj/xcuserdata/garchinskyr1.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | ShootingStar.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 301D6B421C28B0C200B67764
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ShootingStar/Assets.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 | }
--------------------------------------------------------------------------------
/ShootingStar/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 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/ShootingStar/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/ShootingStar/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // ShootingStar
4 | //
5 | // Created by Ryan Garchinsky on 12/21/15.
6 | // Copyright © 2015 garapps. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import "ViewController.h"
11 |
12 | @interface AppDelegate ()
13 |
14 | @end
15 |
16 | @implementation AppDelegate
17 |
18 |
19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
20 |
21 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
22 | self.window.rootViewController = [[ViewController alloc] init];
23 | [self.window makeKeyAndVisible];
24 |
25 | return YES;
26 | }
27 |
28 | - (void)applicationWillResignActive:(UIApplication *)application {
29 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
30 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
31 | }
32 |
33 | - (void)applicationDidEnterBackground:(UIApplication *)application {
34 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
35 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
36 | }
37 |
38 | - (void)applicationWillEnterForeground:(UIApplication *)application {
39 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
40 | }
41 |
42 | - (void)applicationDidBecomeActive:(UIApplication *)application {
43 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
44 | }
45 |
46 | - (void)applicationWillTerminate:(UIApplication *)application {
47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
48 | }
49 |
50 | @end
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ShootingStars
2 | Some fun Shooting Star animations built with both SpriteKit and CAEmitterLayer. Run the example project to see both in action.
3 |
4 | There are 2 ways to achieve a shooting star animation, which travels along a defined path while emitting particles. There are both some advantages, and disadvantages for each method.
5 |
6 | ## Sprite Kit Example
7 | 
8 | ### Advantages
9 | - Particle emitters are much more flexible. You can attach a `targetNode` to your sprite's emitter, which allows your particles to become independent of the sprite, thus not moving along the same path as the sprite. This is not as easily achievable using `CAEmitterLayer`
10 | - Sprite Particles (`.sks` files in Xcode) come with a very nice preview playground, where you can tweak values of the particles and see the changes real-time, and not have to keep restarting your app to see the changes.
11 | - End result seems much smoother
12 |
13 | ### Disadvantages
14 | - You must add a `SKView` and associated `SKScene` on top of your view. `SKScene`'s have very different coordinate systems than regular `UIView`s. [Read more from Apple here](https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Nodes/Nodes.html). This means you may carefully translate coordinates from a `UIView` to a `SKScene`
15 | - Performance wise, using Sprite Kit seems to lag much easier (at least with this example). For example, in the sample, try to shoot off 5 SpriteKit stars, and then shoot off 5 Core Animation stars. You'll notice a lot of lag on the SpriteKit star, but not much on the CoreAnimation star
16 |
17 | ## Core Animation Example
18 | 
19 | ### Advantages
20 | - You don't need to worry about thinking about converting coordinates to a points in a `SKScene`, or worry about adding a `SKView` on top of your current `UIView`
21 | - Performance (at least for this example) seems much better. You can shoot off many CA stars with little to no lag
22 |
23 | ### Disadvantages
24 | - Emitters do not have a nice playground where you can easily tweak settings and see the result real time. You must either keep restarting your app, or purchase an app that allows you to tweak settings and see the result real time.
25 | - Emitter particles, when attached to a `UIView`, follow the same path the `UIView` does, resulting in some ugly animations of your `UIView` does lots of turns with particles.
26 |
27 | ## TODO:
28 | - Swift Example
29 |
30 |
--------------------------------------------------------------------------------
/ShootingStar.xcodeproj/xcuserdata/garchinskyr1.xcuserdatad/xcschemes/ShootingStar.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/ShootingStar/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // ShootingStar
4 | //
5 | // Created by Ryan Garchinsky on 12/21/15.
6 | // Copyright © 2015 garapps. All rights reserved.
7 | //
8 |
9 | #define SCREEN_SCALE [[UIScreen mainScreen] scale]
10 | #define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
11 |
12 | #import "ViewController.h"
13 | #import
14 |
15 | @interface ViewController ()
16 |
17 | // Sprite Kit
18 | @property (nonatomic, strong) UIButton *fireSpriteKitButton;
19 | @property (nonatomic, strong) UIButton *fireCoreAnimationButton;
20 |
21 | //Core Animation
22 | @property (nonatomic, strong) UIImageView *caStarImageView;
23 | @property (nonatomic, strong) CAEmitterLayer *caStarEmitter;
24 |
25 | @end
26 |
27 | @implementation ViewController
28 |
29 | - (instancetype)init {
30 | self = [super init];
31 |
32 | if (self) {
33 | self.view.backgroundColor = [UIColor colorWithRed:0.57 green:0.66 blue:0.74 alpha:1];
34 |
35 | self.fireSpriteKitButton = [UIButton buttonWithType:UIButtonTypeCustom];
36 | self.fireSpriteKitButton.translatesAutoresizingMaskIntoConstraints = NO;
37 | [self.fireSpriteKitButton setTitle:@"Sprite Kit" forState:UIControlStateNormal];
38 | [self.fireSpriteKitButton setTitleColor:[UIColor grayColor] forState:UIControlStateHighlighted];
39 | [self.fireSpriteKitButton setBackgroundColor:[[UIColor whiteColor] colorWithAlphaComponent:0.3f]];
40 | [self.fireSpriteKitButton setTintColor:[UIColor grayColor]];
41 | [self.fireSpriteKitButton addTarget:self action:@selector(shootOffSpriteKitStarFromView:) forControlEvents:UIControlEventTouchUpInside];
42 |
43 | self.fireCoreAnimationButton = [UIButton buttonWithType:UIButtonTypeCustom];
44 | self.fireCoreAnimationButton.translatesAutoresizingMaskIntoConstraints = NO;
45 | [self.fireCoreAnimationButton setTitle:@"Core Animation" forState:UIControlStateNormal];
46 | [self.fireCoreAnimationButton setTitleColor:[UIColor grayColor] forState:UIControlStateHighlighted];
47 | [self.fireCoreAnimationButton setBackgroundColor:[[UIColor redColor] colorWithAlphaComponent:0.3f]];
48 | [self.fireCoreAnimationButton setTintColor:[UIColor grayColor]];
49 | [self.fireCoreAnimationButton addTarget:self action:@selector(shootOffCoreAnimationStarFromView:) forControlEvents:UIControlEventTouchUpInside];
50 |
51 | [self.view addSubview:self.fireSpriteKitButton];
52 | [self.view addSubview:self.fireCoreAnimationButton];
53 |
54 | NSDictionary *views = NSDictionaryOfVariableBindings(_fireSpriteKitButton, _fireCoreAnimationButton);
55 |
56 | [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_fireSpriteKitButton(100)]|" options:0 metrics:nil views:views]];
57 |
58 | [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_fireSpriteKitButton][_fireCoreAnimationButton]|" options:NSLayoutFormatAlignAllBottom|NSLayoutFormatAlignAllTop metrics:nil views:views]];
59 |
60 | [NSLayoutConstraint activateConstraints:@[[NSLayoutConstraint constraintWithItem:self.fireCoreAnimationButton attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.fireSpriteKitButton attribute:NSLayoutAttributeWidth multiplier:1 constant:0]]];
61 | }
62 |
63 | return self;
64 | }
65 |
66 | // Sprite Kit Example
67 | - (void)shootOffSpriteKitStarFromView:(UIView *)view {
68 | CGPoint viewOrigin;
69 |
70 | viewOrigin.y = 0;
71 | viewOrigin.x = (view.frame.origin.x + (view.frame.size.width / 2)) / SCREEN_SCALE;
72 |
73 | UIView *containerView = [[UIView alloc] initWithFrame:self.view.bounds];
74 | containerView.userInteractionEnabled = NO;
75 |
76 | SKView *skView = [[SKView alloc] initWithFrame:containerView.frame];
77 | skView.allowsTransparency = YES;
78 | [containerView addSubview:skView];
79 |
80 | SKScene *skScene = [SKScene sceneWithSize:skView.frame.size];
81 | skScene.scaleMode = SKSceneScaleModeFill;
82 | skScene.backgroundColor = [UIColor clearColor];
83 |
84 | SKSpriteNode *starSprite = [SKSpriteNode spriteNodeWithImageNamed:@"filled_star"];
85 | [starSprite setScale:0.6];
86 | starSprite.position = viewOrigin;
87 | [skScene addChild:starSprite];
88 |
89 | SKEmitterNode *emitter = [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:@"StarParticle" ofType:@"sks"]];
90 | SKEmitterNode *dotEmitter = [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:@"AsteriskParticle" ofType:@"sks"]];
91 |
92 | [dotEmitter setParticlePosition:CGPointMake(0, -starSprite.size.height)];
93 | [emitter setParticlePosition:CGPointMake(0, -starSprite.size.height)];
94 |
95 | emitter.targetNode = skScene;
96 | dotEmitter.targetNode = skScene;
97 |
98 | [starSprite addChild:emitter];
99 | [starSprite addChild:dotEmitter];
100 |
101 | [skView presentScene:skScene];
102 |
103 | CGMutablePathRef path = CGPathCreateMutable();
104 | CGPathMoveToPoint(path, NULL, viewOrigin.x, viewOrigin.y);
105 |
106 | CGPoint endPoint = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height + 100);
107 | UIBezierPath *bp = [UIBezierPath new];
108 | [bp moveToPoint:viewOrigin];
109 |
110 | // curvy path
111 | // control points "pull" the curve to that point on the screen. You should be smarter then just using magic numbers like below.
112 | [bp addCurveToPoint:endPoint controlPoint1:CGPointMake(viewOrigin.x + 500, viewOrigin.y + 275) controlPoint2:CGPointMake(-200, skView.frame.size.height - 250)];
113 |
114 | __weak typeof(containerView) weakView = containerView;
115 | SKAction *followline = [SKAction followPath:bp.CGPath asOffset:YES orientToPath:YES duration:3.0];
116 |
117 | SKAction *done = [SKAction runBlock:^{
118 | // lets delay until all particles are removed
119 | int64_t delayInSeconds = 2.2;
120 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
121 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
122 | [weakView removeFromSuperview];
123 | });
124 | }];
125 |
126 | [starSprite runAction:[SKAction sequence:@[followline, done]]];
127 |
128 | [self.view addSubview:containerView];
129 | }
130 |
131 |
132 |
133 | // Core Animation Example
134 | - (void)shootOffCoreAnimationStarFromView:(UIView *)view {
135 | self.caStarImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"filled_star"]];
136 | self.caStarImageView.alpha = 1.0f;
137 | CGRect imageFrame = CGRectMake(self.caStarImageView.frame.origin.x, self.caStarImageView.frame.origin.y, 50, 50);
138 |
139 | //Your image frame.origin from where the animation need to get start
140 | CGPoint viewOrigin = self.caStarImageView.frame.origin;
141 |
142 | viewOrigin.y = self.view.frame.size.height;
143 | viewOrigin.x = (view.frame.origin.x + (view.frame.size.width / 2));
144 |
145 | self.caStarImageView.frame = imageFrame;
146 | self.caStarImageView.layer.position = viewOrigin;
147 | [self.view addSubview:self.caStarImageView];
148 |
149 | // need to rotate the image to get it on the right tangent
150 | self.caStarImageView.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(90));
151 |
152 | // particles emitters
153 | self.caStarEmitter = [CAEmitterLayer new];
154 | self.caStarEmitter.emitterPosition = CGPointMake((self.caStarImageView.frame.size.width / 2) - 5, self.caStarImageView.frame.size.height);
155 | self.caStarEmitter.emitterShape = kCAEmitterLayerLine;
156 | self.caStarEmitter.emitterZPosition = 10; // 3;
157 | self.caStarEmitter.emitterSize = CGSizeMake(0.5, 0.5);
158 |
159 | CAEmitterCell *star = [self makeEmitterCellWithShape:0];
160 | CAEmitterCell *star2 = [self makeEmitterCellWithShape:0];
161 | CAEmitterCell *star3 = [self makeEmitterCellWithShape:0];
162 |
163 | CAEmitterCell *circle = [self makeEmitterCellWithShape:1];
164 | CAEmitterCell *circle2 = [self makeEmitterCellWithShape:1];
165 | CAEmitterCell *circle3 = [self makeEmitterCellWithShape:1];
166 |
167 | self.caStarEmitter.emitterCells = @[star, star2, star3, circle, circle2, circle3];
168 | [self.caStarImageView.layer addSublayer:self.caStarEmitter];
169 |
170 | // Set up fade out effect
171 | CABasicAnimation *fadeOutAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
172 | [fadeOutAnimation setToValue:[NSNumber numberWithFloat:1.0]];
173 | fadeOutAnimation.fillMode = kCAFillModeForwards;
174 | fadeOutAnimation.removedOnCompletion = NO;
175 |
176 | // Set up path movement
177 | CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
178 | pathAnimation.calculationMode = kCAAnimationCubicPaced;
179 | pathAnimation.fillMode = kCAFillModeForwards;
180 | pathAnimation.removedOnCompletion = NO;
181 | pathAnimation.rotationMode = kCAAnimationRotateAuto;
182 |
183 | //Setting Endpoint of the animation
184 | CGPoint endPoint = CGPointMake(self.view.frame.size.width, -100);
185 | UIBezierPath *bp = [UIBezierPath new];
186 | [bp moveToPoint:viewOrigin];
187 |
188 | // control points "pull" the curve to that point on the screen. You should be smarter then just using magic numbers like below.
189 | [bp addCurveToPoint:endPoint controlPoint1:CGPointMake(endPoint.x - 400, endPoint.y + 200) controlPoint2:endPoint];
190 |
191 | pathAnimation.path = bp.CGPath;
192 |
193 | CAAnimationGroup *group = [CAAnimationGroup animation];
194 | group.fillMode = kCAFillModeForwards;
195 | group.removedOnCompletion = NO;
196 | [group setAnimations:[NSArray arrayWithObjects:fadeOutAnimation, pathAnimation, nil]];
197 | group.duration = 2.3f;
198 | group.delegate = self;
199 | [group setValue:self.caStarImageView forKey:@"imageViewBeingAnimated"];
200 |
201 | [self.caStarImageView.layer addAnimation:group forKey:@"savingAnimation"];
202 | }
203 |
204 | - (CAEmitterCell *)makeEmitterCellWithShape:(NSInteger)shape {
205 | CAEmitterCell *cell = [CAEmitterCell new];
206 | cell.velocity = [self randFloatBetween:125 and:200];
207 | cell.velocityRange = [self randFloatBetween:15 and:40];
208 | cell.emissionLongitude = M_PI;
209 | cell.emissionRange = M_PI_4;
210 | cell.spin = 2;
211 | cell.spinRange = 10;
212 | cell.scale = 0.2;
213 | cell.scaleSpeed = -0.05;
214 |
215 | cell.lifetime = [self randFloatBetween:0.3 and:0.75];
216 | cell.lifetimeRange = [self randFloatBetween:0.6 and:1.5];
217 |
218 | if (shape == 0) {
219 | cell.color = [UIColor yellowColor].CGColor;
220 | cell.contents = (id)[[UIImage imageNamed:@"filled_star"] CGImage];
221 | cell.birthRate = 15;
222 | }
223 | else {
224 | cell.color = [UIColor orangeColor].CGColor;
225 | cell.contents = (id)[[UIImage imageNamed:@"asterisk_filled"] CGImage];
226 | cell.birthRate = 40;
227 | }
228 |
229 | return cell;
230 | }
231 |
232 | - (CGFloat)randFloatBetween:(float)low and:(float)high {
233 | float diff = high - low;
234 | return (((float) rand() / RAND_MAX) * diff) + low;
235 | }
236 |
237 | #pragma mark - CAAnimationGroup Delegate
238 | - (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished {
239 | if (finished) {
240 | // stop emitting particles, and wait a couple seconds so they all have time to disappear
241 | self.caStarEmitter.birthRate = 0;
242 |
243 | __weak typeof(self) weakSelf = self;
244 | int64_t delayInSeconds = 1.5;
245 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
246 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
247 | [weakSelf.caStarImageView removeFromSuperview];
248 | });
249 | }
250 | }
251 |
252 | @end
253 |
--------------------------------------------------------------------------------
/ShootingStar.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 301D6B481C28B0C200B67764 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 301D6B471C28B0C200B67764 /* main.m */; };
11 | 301D6B4B1C28B0C200B67764 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 301D6B4A1C28B0C200B67764 /* AppDelegate.m */; };
12 | 301D6B4E1C28B0C200B67764 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 301D6B4D1C28B0C200B67764 /* ViewController.m */; };
13 | 301D6B531C28B0C200B67764 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 301D6B521C28B0C200B67764 /* Assets.xcassets */; };
14 | 301D6B561C28B0C200B67764 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 301D6B541C28B0C200B67764 /* LaunchScreen.storyboard */; };
15 | 301D6B5F1C28FF5200B67764 /* AsteriskParticle.sks in Resources */ = {isa = PBXBuildFile; fileRef = 301D6B5D1C28FF5200B67764 /* AsteriskParticle.sks */; };
16 | 301D6B601C28FF5200B67764 /* StarParticle.sks in Resources */ = {isa = PBXBuildFile; fileRef = 301D6B5E1C28FF5200B67764 /* StarParticle.sks */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXFileReference section */
20 | 301D6B431C28B0C200B67764 /* ShootingStar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ShootingStar.app; sourceTree = BUILT_PRODUCTS_DIR; };
21 | 301D6B471C28B0C200B67764 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
22 | 301D6B491C28B0C200B67764 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
23 | 301D6B4A1C28B0C200B67764 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
24 | 301D6B4C1C28B0C200B67764 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
25 | 301D6B4D1C28B0C200B67764 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
26 | 301D6B521C28B0C200B67764 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
27 | 301D6B551C28B0C200B67764 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
28 | 301D6B571C28B0C200B67764 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
29 | 301D6B5D1C28FF5200B67764 /* AsteriskParticle.sks */ = {isa = PBXFileReference; lastKnownFileType = file.sks; path = AsteriskParticle.sks; sourceTree = ""; };
30 | 301D6B5E1C28FF5200B67764 /* StarParticle.sks */ = {isa = PBXFileReference; lastKnownFileType = file.sks; path = StarParticle.sks; sourceTree = ""; };
31 | /* End PBXFileReference section */
32 |
33 | /* Begin PBXFrameworksBuildPhase section */
34 | 301D6B401C28B0C200B67764 /* Frameworks */ = {
35 | isa = PBXFrameworksBuildPhase;
36 | buildActionMask = 2147483647;
37 | files = (
38 | );
39 | runOnlyForDeploymentPostprocessing = 0;
40 | };
41 | /* End PBXFrameworksBuildPhase section */
42 |
43 | /* Begin PBXGroup section */
44 | 301D6B3A1C28B0C200B67764 = {
45 | isa = PBXGroup;
46 | children = (
47 | 301D6B451C28B0C200B67764 /* ShootingStar */,
48 | 301D6B441C28B0C200B67764 /* Products */,
49 | );
50 | sourceTree = "";
51 | };
52 | 301D6B441C28B0C200B67764 /* Products */ = {
53 | isa = PBXGroup;
54 | children = (
55 | 301D6B431C28B0C200B67764 /* ShootingStar.app */,
56 | );
57 | name = Products;
58 | sourceTree = "";
59 | };
60 | 301D6B451C28B0C200B67764 /* ShootingStar */ = {
61 | isa = PBXGroup;
62 | children = (
63 | 301D6B5D1C28FF5200B67764 /* AsteriskParticle.sks */,
64 | 301D6B5E1C28FF5200B67764 /* StarParticle.sks */,
65 | 301D6B491C28B0C200B67764 /* AppDelegate.h */,
66 | 301D6B4A1C28B0C200B67764 /* AppDelegate.m */,
67 | 301D6B4C1C28B0C200B67764 /* ViewController.h */,
68 | 301D6B4D1C28B0C200B67764 /* ViewController.m */,
69 | 301D6B521C28B0C200B67764 /* Assets.xcassets */,
70 | 301D6B541C28B0C200B67764 /* LaunchScreen.storyboard */,
71 | 301D6B571C28B0C200B67764 /* Info.plist */,
72 | 301D6B461C28B0C200B67764 /* Supporting Files */,
73 | );
74 | path = ShootingStar;
75 | sourceTree = "";
76 | };
77 | 301D6B461C28B0C200B67764 /* Supporting Files */ = {
78 | isa = PBXGroup;
79 | children = (
80 | 301D6B471C28B0C200B67764 /* main.m */,
81 | );
82 | name = "Supporting Files";
83 | sourceTree = "";
84 | };
85 | /* End PBXGroup section */
86 |
87 | /* Begin PBXNativeTarget section */
88 | 301D6B421C28B0C200B67764 /* ShootingStar */ = {
89 | isa = PBXNativeTarget;
90 | buildConfigurationList = 301D6B5A1C28B0C200B67764 /* Build configuration list for PBXNativeTarget "ShootingStar" */;
91 | buildPhases = (
92 | 301D6B3F1C28B0C200B67764 /* Sources */,
93 | 301D6B401C28B0C200B67764 /* Frameworks */,
94 | 301D6B411C28B0C200B67764 /* Resources */,
95 | );
96 | buildRules = (
97 | );
98 | dependencies = (
99 | );
100 | name = ShootingStar;
101 | productName = ShootingStar;
102 | productReference = 301D6B431C28B0C200B67764 /* ShootingStar.app */;
103 | productType = "com.apple.product-type.application";
104 | };
105 | /* End PBXNativeTarget section */
106 |
107 | /* Begin PBXProject section */
108 | 301D6B3B1C28B0C200B67764 /* Project object */ = {
109 | isa = PBXProject;
110 | attributes = {
111 | LastUpgradeCheck = 0720;
112 | ORGANIZATIONNAME = garapps;
113 | TargetAttributes = {
114 | 301D6B421C28B0C200B67764 = {
115 | CreatedOnToolsVersion = 7.2;
116 | };
117 | };
118 | };
119 | buildConfigurationList = 301D6B3E1C28B0C200B67764 /* Build configuration list for PBXProject "ShootingStar" */;
120 | compatibilityVersion = "Xcode 3.2";
121 | developmentRegion = English;
122 | hasScannedForEncodings = 0;
123 | knownRegions = (
124 | en,
125 | Base,
126 | );
127 | mainGroup = 301D6B3A1C28B0C200B67764;
128 | productRefGroup = 301D6B441C28B0C200B67764 /* Products */;
129 | projectDirPath = "";
130 | projectRoot = "";
131 | targets = (
132 | 301D6B421C28B0C200B67764 /* ShootingStar */,
133 | );
134 | };
135 | /* End PBXProject section */
136 |
137 | /* Begin PBXResourcesBuildPhase section */
138 | 301D6B411C28B0C200B67764 /* Resources */ = {
139 | isa = PBXResourcesBuildPhase;
140 | buildActionMask = 2147483647;
141 | files = (
142 | 301D6B561C28B0C200B67764 /* LaunchScreen.storyboard in Resources */,
143 | 301D6B601C28FF5200B67764 /* StarParticle.sks in Resources */,
144 | 301D6B5F1C28FF5200B67764 /* AsteriskParticle.sks in Resources */,
145 | 301D6B531C28B0C200B67764 /* Assets.xcassets in Resources */,
146 | );
147 | runOnlyForDeploymentPostprocessing = 0;
148 | };
149 | /* End PBXResourcesBuildPhase section */
150 |
151 | /* Begin PBXSourcesBuildPhase section */
152 | 301D6B3F1C28B0C200B67764 /* Sources */ = {
153 | isa = PBXSourcesBuildPhase;
154 | buildActionMask = 2147483647;
155 | files = (
156 | 301D6B4E1C28B0C200B67764 /* ViewController.m in Sources */,
157 | 301D6B4B1C28B0C200B67764 /* AppDelegate.m in Sources */,
158 | 301D6B481C28B0C200B67764 /* main.m in Sources */,
159 | );
160 | runOnlyForDeploymentPostprocessing = 0;
161 | };
162 | /* End PBXSourcesBuildPhase section */
163 |
164 | /* Begin PBXVariantGroup section */
165 | 301D6B541C28B0C200B67764 /* LaunchScreen.storyboard */ = {
166 | isa = PBXVariantGroup;
167 | children = (
168 | 301D6B551C28B0C200B67764 /* Base */,
169 | );
170 | name = LaunchScreen.storyboard;
171 | sourceTree = "";
172 | };
173 | /* End PBXVariantGroup section */
174 |
175 | /* Begin XCBuildConfiguration section */
176 | 301D6B581C28B0C200B67764 /* Debug */ = {
177 | isa = XCBuildConfiguration;
178 | buildSettings = {
179 | ALWAYS_SEARCH_USER_PATHS = NO;
180 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
181 | CLANG_CXX_LIBRARY = "libc++";
182 | CLANG_ENABLE_MODULES = YES;
183 | CLANG_ENABLE_OBJC_ARC = YES;
184 | CLANG_WARN_BOOL_CONVERSION = YES;
185 | CLANG_WARN_CONSTANT_CONVERSION = YES;
186 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
187 | CLANG_WARN_EMPTY_BODY = YES;
188 | CLANG_WARN_ENUM_CONVERSION = YES;
189 | CLANG_WARN_INT_CONVERSION = YES;
190 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
191 | CLANG_WARN_UNREACHABLE_CODE = YES;
192 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
193 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
194 | COPY_PHASE_STRIP = NO;
195 | DEBUG_INFORMATION_FORMAT = dwarf;
196 | ENABLE_STRICT_OBJC_MSGSEND = YES;
197 | ENABLE_TESTABILITY = YES;
198 | GCC_C_LANGUAGE_STANDARD = gnu99;
199 | GCC_DYNAMIC_NO_PIC = NO;
200 | GCC_NO_COMMON_BLOCKS = YES;
201 | GCC_OPTIMIZATION_LEVEL = 0;
202 | GCC_PREPROCESSOR_DEFINITIONS = (
203 | "DEBUG=1",
204 | "$(inherited)",
205 | );
206 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
207 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
208 | GCC_WARN_UNDECLARED_SELECTOR = YES;
209 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
210 | GCC_WARN_UNUSED_FUNCTION = YES;
211 | GCC_WARN_UNUSED_VARIABLE = YES;
212 | IPHONEOS_DEPLOYMENT_TARGET = 9.2;
213 | MTL_ENABLE_DEBUG_INFO = YES;
214 | ONLY_ACTIVE_ARCH = YES;
215 | SDKROOT = iphoneos;
216 | };
217 | name = Debug;
218 | };
219 | 301D6B591C28B0C200B67764 /* Release */ = {
220 | isa = XCBuildConfiguration;
221 | buildSettings = {
222 | ALWAYS_SEARCH_USER_PATHS = NO;
223 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
224 | CLANG_CXX_LIBRARY = "libc++";
225 | CLANG_ENABLE_MODULES = YES;
226 | CLANG_ENABLE_OBJC_ARC = YES;
227 | CLANG_WARN_BOOL_CONVERSION = YES;
228 | CLANG_WARN_CONSTANT_CONVERSION = YES;
229 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
230 | CLANG_WARN_EMPTY_BODY = YES;
231 | CLANG_WARN_ENUM_CONVERSION = YES;
232 | CLANG_WARN_INT_CONVERSION = YES;
233 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
234 | CLANG_WARN_UNREACHABLE_CODE = YES;
235 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
236 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
237 | COPY_PHASE_STRIP = NO;
238 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
239 | ENABLE_NS_ASSERTIONS = NO;
240 | ENABLE_STRICT_OBJC_MSGSEND = YES;
241 | GCC_C_LANGUAGE_STANDARD = gnu99;
242 | GCC_NO_COMMON_BLOCKS = YES;
243 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
244 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
245 | GCC_WARN_UNDECLARED_SELECTOR = YES;
246 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
247 | GCC_WARN_UNUSED_FUNCTION = YES;
248 | GCC_WARN_UNUSED_VARIABLE = YES;
249 | IPHONEOS_DEPLOYMENT_TARGET = 9.2;
250 | MTL_ENABLE_DEBUG_INFO = NO;
251 | SDKROOT = iphoneos;
252 | VALIDATE_PRODUCT = YES;
253 | };
254 | name = Release;
255 | };
256 | 301D6B5B1C28B0C200B67764 /* Debug */ = {
257 | isa = XCBuildConfiguration;
258 | buildSettings = {
259 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
260 | INFOPLIST_FILE = ShootingStar/Info.plist;
261 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
262 | PRODUCT_BUNDLE_IDENTIFIER = garapps.ShootingStar;
263 | PRODUCT_NAME = "$(TARGET_NAME)";
264 | };
265 | name = Debug;
266 | };
267 | 301D6B5C1C28B0C200B67764 /* Release */ = {
268 | isa = XCBuildConfiguration;
269 | buildSettings = {
270 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
271 | INFOPLIST_FILE = ShootingStar/Info.plist;
272 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
273 | PRODUCT_BUNDLE_IDENTIFIER = garapps.ShootingStar;
274 | PRODUCT_NAME = "$(TARGET_NAME)";
275 | };
276 | name = Release;
277 | };
278 | /* End XCBuildConfiguration section */
279 |
280 | /* Begin XCConfigurationList section */
281 | 301D6B3E1C28B0C200B67764 /* Build configuration list for PBXProject "ShootingStar" */ = {
282 | isa = XCConfigurationList;
283 | buildConfigurations = (
284 | 301D6B581C28B0C200B67764 /* Debug */,
285 | 301D6B591C28B0C200B67764 /* Release */,
286 | );
287 | defaultConfigurationIsVisible = 0;
288 | defaultConfigurationName = Release;
289 | };
290 | 301D6B5A1C28B0C200B67764 /* Build configuration list for PBXNativeTarget "ShootingStar" */ = {
291 | isa = XCConfigurationList;
292 | buildConfigurations = (
293 | 301D6B5B1C28B0C200B67764 /* Debug */,
294 | 301D6B5C1C28B0C200B67764 /* Release */,
295 | );
296 | defaultConfigurationIsVisible = 0;
297 | defaultConfigurationName = Release;
298 | };
299 | /* End XCConfigurationList section */
300 | };
301 | rootObject = 301D6B3B1C28B0C200B67764 /* Project object */;
302 | }
303 |
--------------------------------------------------------------------------------