Skip to content

Commit

Permalink
snowflakes animation
Browse files Browse the repository at this point in the history
  • Loading branch information
Artur Rymarz committed Apr 24, 2018
1 parent 8754a14 commit 52adb8e
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 4 deletions.
24 changes: 24 additions & 0 deletions Demo/Demo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
0BBBF5F52088A4DB001F0C00 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0BBBF5F32088A4DB001F0C00 /* Main.storyboard */; };
0BBBF5F72088A4DC001F0C00 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0BBBF5F62088A4DC001F0C00 /* Assets.xcassets */; };
0BBBF5FA2088A4DC001F0C00 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0BBBF5F82088A4DC001F0C00 /* LaunchScreen.storyboard */; };
0BCE8460208F3732004CDF2D /* SnowflakesAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BCE845F208F3732004CDF2D /* SnowflakesAnimation.swift */; };
0BCE8462208F3865004CDF2D /* particle.png in Resources */ = {isa = PBXBuildFile; fileRef = 0BCE8461208F3865004CDF2D /* particle.png */; };
0BCE8464208F399A004CDF2D /* snowflake.png in Resources */ = {isa = PBXBuildFile; fileRef = 0BCE8463208F399A004CDF2D /* snowflake.png */; };
0BCE8466208F4096004CDF2D /* SnowflakeSpeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BCE8465208F4096004CDF2D /* SnowflakeSpeed.swift */; };
0BDA574F2089DFF40037BD2C /* GlassBreakAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BDA57492089DFF40037BD2C /* GlassBreakAnimation.swift */; };
0BDA57502089DFF40037BD2C /* GlassPiece.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BDA574B2089DFF40037BD2C /* GlassPiece.swift */; };
0BDA57512089DFF40037BD2C /* GridSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BDA574C2089DFF40037BD2C /* GridSize.swift */; };
Expand All @@ -29,6 +33,10 @@
0BBBF5F62088A4DC001F0C00 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
0BBBF5F92088A4DC001F0C00 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
0BBBF5FB2088A4DC001F0C00 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0BCE845F208F3732004CDF2D /* SnowflakesAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnowflakesAnimation.swift; sourceTree = "<group>"; };
0BCE8461208F3865004CDF2D /* particle.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = particle.png; sourceTree = "<group>"; };
0BCE8463208F399A004CDF2D /* snowflake.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = snowflake.png; sourceTree = "<group>"; };
0BCE8465208F4096004CDF2D /* SnowflakeSpeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnowflakeSpeed.swift; sourceTree = "<group>"; };
0BDA57492089DFF40037BD2C /* GlassBreakAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlassBreakAnimation.swift; sourceTree = "<group>"; };
0BDA574B2089DFF40037BD2C /* GlassPiece.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlassPiece.swift; sourceTree = "<group>"; };
0BDA574C2089DFF40037BD2C /* GridSize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridSize.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -70,6 +78,8 @@
isa = PBXGroup;
children = (
0BBBF5EF2088A4DB001F0C00 /* AppDelegate.swift */,
0BCE8461208F3865004CDF2D /* particle.png */,
0BCE8463208F399A004CDF2D /* snowflake.png */,
0BBBF5F12088A4DB001F0C00 /* ViewController.swift */,
0BBBF5F32088A4DB001F0C00 /* Main.storyboard */,
0BBBF5F62088A4DC001F0C00 /* Assets.xcassets */,
Expand All @@ -79,9 +89,19 @@
path = Demo;
sourceTree = "<group>";
};
0BCE845E208F3720004CDF2D /* Snowflakes */ = {
isa = PBXGroup;
children = (
0BCE845F208F3732004CDF2D /* SnowflakesAnimation.swift */,
0BCE8465208F4096004CDF2D /* SnowflakeSpeed.swift */,
);
path = Snowflakes;
sourceTree = "<group>";
};
0BDA57482089DFF40037BD2C /* Source */ = {
isa = PBXGroup;
children = (
0BCE845E208F3720004CDF2D /* Snowflakes */,
14A66169208BE2AA0081091A /* Explosion */,
0BDA574A2089DFF40037BD2C /* GlassBreak */,
0BDA574C2089DFF40037BD2C /* GridSize.swift */,
Expand Down Expand Up @@ -168,9 +188,11 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0BCE8464208F399A004CDF2D /* snowflake.png in Resources */,
0BBBF5FA2088A4DC001F0C00 /* LaunchScreen.storyboard in Resources */,
0BBBF5F72088A4DC001F0C00 /* Assets.xcassets in Resources */,
0BBBF5F52088A4DB001F0C00 /* Main.storyboard in Resources */,
0BCE8462208F3865004CDF2D /* particle.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -184,8 +206,10 @@
0BDA57532089DFF40037BD2C /* ClosedRangeExtension.swift in Sources */,
0BDA57512089DFF40037BD2C /* GridSize.swift in Sources */,
14A6616D208BE3160081091A /* ExplosionPiece.swift in Sources */,
0BCE8460208F3732004CDF2D /* SnowflakesAnimation.swift in Sources */,
0BDA57502089DFF40037BD2C /* GlassPiece.swift in Sources */,
0BBBF5F22088A4DB001F0C00 /* ViewController.swift in Sources */,
0BCE8466208F4096004CDF2D /* SnowflakeSpeed.swift in Sources */,
14A6616B208BE2BB0081091A /* ExplosionAnimation.swift in Sources */,
0BBBF5F02088A4DB001F0C00 /* AppDelegate.swift in Sources */,
0BDA57522089DFF40037BD2C /* UIViewExtensions.swift in Sources */,
Expand Down
20 changes: 16 additions & 4 deletions Demo/Demo/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
Expand All @@ -19,36 +20,47 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="749" image="Sample" translatesAutoresizingMaskIntoConstraints="NO" id="W93-LT-woS">
<rect key="frame" x="8" y="50" width="359" height="600"/>
<rect key="frame" x="8" y="200.5" width="359" height="266"/>
<constraints>
<constraint firstAttribute="width" secondItem="W93-LT-woS" secondAttribute="height" multiplier="800:600" constant="4" id="WYa-2U-0nQ"/>
</constraints>
</imageView>
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="bbL-RR-J8Z">
<rect key="frame" x="0.0" y="20" width="375" height="30"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Vfm-57-d8e">
<rect key="frame" x="0.0" y="0.0" width="187.5" height="30"/>
<rect key="frame" x="0.0" y="0.0" width="125" height="30"/>
<state key="normal" title="Break Glass"/>
<connections>
<action selector="breakGlass:" destination="BYZ-38-t0r" eventType="touchUpInside" id="BEI-GO-x1B"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1nr-Li-yaj">
<rect key="frame" x="187.5" y="0.0" width="187.5" height="30"/>
<rect key="frame" x="125" y="0.0" width="125" height="30"/>
<state key="normal" title="Explode"/>
<connections>
<action selector="explode:" destination="BYZ-38-t0r" eventType="touchUpInside" id="eYC-72-ZoQ"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="lol-E1-qvP">
<rect key="frame" x="250" y="0.0" width="125" height="30"/>
<state key="normal" title="Snowflakes"/>
<connections>
<action selector="showSnowflakes:" destination="BYZ-38-t0r" eventType="touchUpInside" id="7sl-xs-JAu"/>
</connections>
</button>
</subviews>
</stackView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="bbL-RR-J8Z" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="FaH-xc-yAe"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="W93-LT-woS" secondAttribute="trailing" constant="8" id="MOH-NV-eXq"/>
<constraint firstItem="W93-LT-woS" firstAttribute="top" secondItem="bbL-RR-J8Z" secondAttribute="bottom" id="RwN-qd-F2P"/>
<constraint firstItem="W93-LT-woS" firstAttribute="top" relation="greaterThanOrEqual" secondItem="bbL-RR-J8Z" secondAttribute="bottom" id="RwN-qd-F2P"/>
<constraint firstItem="bbL-RR-J8Z" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="m0D-eT-8PK"/>
<constraint firstItem="W93-LT-woS" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="8" id="miy-Eu-FdJ"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="bbL-RR-J8Z" secondAttribute="trailing" id="p01-pc-3Ix"/>
<constraint firstItem="W93-LT-woS" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="pmC-9f-JBE"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="W93-LT-woS" secondAttribute="bottom" constant="8" id="ubu-t4-gpU"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
Expand Down
4 changes: 4 additions & 0 deletions Demo/Demo/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ class ViewController: UIViewController {
})
}

@IBAction func showSnowflakes(_ sender: Any) {
exampleView?.addSnowflakes(amount: 10, speed: .slow)
}

private func reshowImage() {
self.exampleView?.alpha = 0
self.exampleView?.isHidden = false
Expand Down
Binary file added Demo/Demo/particle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Demo/Demo/snowflake.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions Source/Snowflakes/SnowflakeSpeed.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// SnowflakeSpeed.swift
// Demo
//
// Created by Artur Rymarz on 24.04.2018.
// Copyright © 2018 Artrmz. All rights reserved.
//

import Foundation

public enum SnowflakeSpeed {
case slow, medium
}
62 changes: 62 additions & 0 deletions Source/Snowflakes/SnowflakesAnimation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// SnowflakesAnimation.swift
// Demo
//
// Created by Artur Rymarz on 24.04.2018.
// Copyright © 2018 Artrmz. All rights reserved.
//

import UIKit

public extension UIView {
/// Adds snowflakes to your view
///
/// - Parameters:
/// - colors: colors of your snowflakes (default: [.white])
/// - amount: amount of snowflakes for each color (default: 3)
/// - speed: soeed of animation (default: .medium)
/// - clipToBounds: should view be clipped to bounds (default: true)
func addSnowflakes(with colors: [UIColor] = [.white], amount: Int = 3, speed: SnowflakeSpeed = .medium, clipToBounds: Bool = true) {
self.clipsToBounds = clipToBounds

let snowflakeEmitter = CAEmitterLayer()

snowflakeEmitter.emitterPosition = CGPoint(x: center.x, y: -96)
snowflakeEmitter.emitterShape = kCAEmitterLayerLine
snowflakeEmitter.emitterSize = CGSize(width: frame.size.width, height: 1)

var cells = [CAEmitterCell]()
colors.forEach { color in
let cell = makeEmitterCell(color: color, amount: amount, speed: speed)
cells.append(cell)
}

snowflakeEmitter.emitterCells = cells
layer.addSublayer(snowflakeEmitter)
}

private func makeEmitterCell(color: UIColor, amount: Int, speed: SnowflakeSpeed) -> CAEmitterCell {
let cell = CAEmitterCell()
cell.birthRate = Float(amount)
cell.lifetime = 7.0
cell.lifetimeRange = 0
cell.color = color.cgColor
let velocity = CGFloat(arc4random_uniform(150) + 150)
switch speed {
case .slow:
cell.velocity = velocity / 3
default:
cell.velocity = velocity
}
cell.velocityRange = CGFloat(arc4random_uniform(20) + 30)
cell.emissionLongitude = CGFloat.pi
cell.emissionRange = CGFloat.pi / 4
cell.spin = CGFloat(arc4random_uniform(2) + 1)
cell.spinRange = CGFloat(arc4random_uniform(5) + 1)
cell.scaleRange = 0.5
cell.scaleSpeed = -0.05

cell.contents = UIImage(named: "snowflake")?.cgImage
return cell
}
}

0 comments on commit 52adb8e

Please sign in to comment.