Skip to content
Permalink
Browse files

add Twinkle

  • Loading branch information...
patrick piemonte
patrick piemonte committed Feb 25, 2015
1 parent 675b4f6 commit 19afe48cb0de75b247a83dd0ebf10a11db816ac2
Showing with 268 additions and 9 deletions.
  1. +2 −6 .gitignore
  2. +0 −1 LICENSE
  3. +60 −2 README.md
  4. BIN Source/Resources/TwinkleImage.png
  5. +192 −0 Source/Twinkle.swift
  6. +14 −0 Twinkle.podspec
@@ -18,9 +18,5 @@ DerivedData
*.xcuserstate

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
#
# Pods/
Pods/
Podfile.lock
@@ -19,4 +19,3 @@ 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.

@@ -1,2 +1,60 @@
# Twinkle
a Swift and easy way to make elements in your iOS app twinkle
## Twinkle

`Twinkle` is a [Swift](https://developer.apple.com/swift/) and easy way to make any UIView in your iOS app twinkle.

This library creates several `CAEmitterLayer` elements and animates them over a few creating a sparkle effect.

## Installation

### CocoaPods

`Twinkle` is available and recommended for installation using the Cocoa dependency manager [CocoaPods](http://cocoapods.org/). CocoaPods is still adding support for Swift, so a [pre-release](http://blog.cocoapods.org/Pod-Authors-Guide-to-CocoaPods-Frameworks/) version is required.

To integrate, just add the following line in your `Podfile`:

```ruby
pod ‘Twinkle
```

### Carthage

Installation is also available using the dependency manager [Carthage](https://github.com/Carthage/Carthage).

To integrate, add the following line to your `Cartfile`:

```ogdl
github “piemonte/Twinkle” >= 0.0.1
```

### Manual

You can also simply copy the `Twinkle.swift` file into your Xcode project.

## Usage

The sample project provides an example of how to integrate `Twinkle`, otherwise you can follow this example.

``` Swift
import Twinkle
```

``` Swift
let view: UIView = UIView(frame: CGRect(x: 0, y: 0, width: 150, height: 50))
self.view.addSubview(view)
view.twinkle()
```

## Community

- Found a bug? Open an [issue](https://github.com/piemonte/twinkle/issues).
- Feature idea? Open an [issue](https://github.com/piemonte/twinkle/issues).
- Want to contribute? Submit a [pull request](https://github.com/piemonte/twinkle/pulls).

## Resources

* [Shimmer](https://github.com/facebook/shimmer)
* [objc.io Issue #16, Swift](http://www.objc.io/issue-16/)

## License

Twinkle is available under the MIT license, see the [LICENSE](https://github.com/piemonte/twinkle/blob/master/LICENSE) file for more information.
Binary file not shown.
@@ -0,0 +1,192 @@
//
// Twinkle.swift
//
// Created by patrick piemonte on 2/20/15.
//
// The MIT License (MIT)
//
// Copyright (c) 2015-present patrick piemonte (http://patrickpiemonte.com/)
//
// 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:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// 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.
import UIKit
import Foundation
import CoreGraphics

private let TwinkleLayerEmitterShapeKey = "circle"
private let TwinkleLayerEmitterModeKey = "surface"
private let TwinkleLayerRenderModeKey = "unordered"
private let TwinkleLayerMagnificationFilter = "linear"
private let TwinkleLayerMinificationFilter = "trilinear"

// MARK: - TwinkleLayer
class TwinkleLayer: CAEmitterLayer {

// MARK: object lifecycle
override init() {
super.init()

// this could be a lot better in terms of performance, but not today
var twinkleImage: UIImage?
let resourcePath: String? = NSBundle.mainBundle().resourcePath
if let filePath = resourcePath?.stringByAppendingPathComponent("TwinkleImage") {
twinkleImage = UIImage(contentsOfFile: filePath)!
}

let emitterCells: [CAEmitterCell] = [CAEmitterCell(), CAEmitterCell()]
for cell in emitterCells {
cell.birthRate = 8
cell.lifetime = 1.25
cell.lifetimeRange = 0
cell.emissionRange = CGFloat(M_PI_4)
cell.velocity = 2
cell.velocityRange = 18
cell.scale = 0.65
cell.scaleRange = 0.7
cell.scaleSpeed = 0.6
cell.spin = 0.9
cell.spinRange = CGFloat(M_PI)
cell.color = UIColor(white: 1.0, alpha: 0.3).CGColor
cell.alphaSpeed = -0.8
cell.contents = twinkleImage?.CGImage
cell.magnificationFilter = TwinkleLayerMagnificationFilter
cell.minificationFilter = TwinkleLayerMinificationFilter
cell.enabled = true
}
self.emitterCells = emitterCells

self.emitterPosition = CGPointMake((bounds.size.width * 0.5), (bounds.size.height * 0.5))
self.emitterSize = bounds.size

self.emitterShape = TwinkleLayerEmitterShapeKey
self.emitterMode = TwinkleLayerEmitterModeKey
self.renderMode = TwinkleLayerRenderModeKey
}

required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)

}

}

// MARK: - TwinkleLayer animations
private let TwinkleLayerPositionAnimationKey = "positionAnimation"
private let TwinkleLayerTransformAnimationKey = "transformAnimation"
private let TwinkleLayerOpacityAnimationKey = "opacityAnimation"

extension TwinkleLayer {

func addPositionAnimation() {
CATransaction.begin()
var keyFrameAnim = CAKeyframeAnimation(keyPath: "position")
keyFrameAnim.duration = 0.3
keyFrameAnim.additive = true
keyFrameAnim.repeatCount = MAXFLOAT
keyFrameAnim.removedOnCompletion = false
keyFrameAnim.beginTime = CFTimeInterval(arc4random_uniform(1000) + 1) * 0.2 * 0.25 // random start time, non-zero
let points: [NSValue] = [NSValue(CGPoint: CGPoint().twinkleRandom(0.25)),
NSValue(CGPoint: CGPoint().twinkleRandom(0.25)),
NSValue(CGPoint: CGPoint().twinkleRandom(0.25)),
NSValue(CGPoint: CGPoint().twinkleRandom(0.25)),
NSValue(CGPoint: CGPoint().twinkleRandom(0.25))]
keyFrameAnim.values = points
self.addAnimation(keyFrameAnim, forKey: TwinkleLayerPositionAnimationKey)
CATransaction.commit()
}

func addRotationAnimation() {
CATransaction.begin()
var keyFrameAnim = CAKeyframeAnimation(keyPath: "transform")
keyFrameAnim.duration = 0.3
keyFrameAnim.valueFunction = CAValueFunction(name: kCAValueFunctionRotateZ)
keyFrameAnim.additive = true
keyFrameAnim.repeatCount = MAXFLOAT
keyFrameAnim.removedOnCompletion = false
keyFrameAnim.beginTime = CFTimeInterval(arc4random_uniform(1000) + 1) * 0.2 * 0.25 // random start time, non-zero
let radians: Float = 0.104 // ~6 degrees
keyFrameAnim.values = [-radians, radians, -radians]
self.addAnimation(keyFrameAnim, forKey: TwinkleLayerTransformAnimationKey)
CATransaction.commit()
}

func addFadeInOutAnimation(beginTime: CFTimeInterval) {
CATransaction.begin()
var fadeAnimation: CABasicAnimation = CABasicAnimation(keyPath: "opacity")
fadeAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
fadeAnimation.fromValue = 0
fadeAnimation.toValue = 1
fadeAnimation.repeatCount = 2
fadeAnimation.autoreverses = true // fade in then out
fadeAnimation.duration = 0.4
fadeAnimation.fillMode = kCAFillModeForwards
fadeAnimation.beginTime = beginTime
CATransaction.setCompletionBlock({
self.removeFromSuperlayer()
})
self.addAnimation(fadeAnimation, forKey: TwinkleLayerOpacityAnimationKey)
CATransaction.commit()
}

}

// MARK: - CGPoint
extension CGPoint {

func twinkleRandom(range: Float)->CGPoint {
let x = Int(-range + (Float(arc4random_uniform(1000)) / 1000.0) * 2.0 * range)
let y = Int(-range + (Float(arc4random_uniform(1000)) / 1000.0) * 2.0 * range)
return CGPoint(x: x, y: y)
}

}

// MARK: - UIView
extension UIView {

func twinkle() {
var twinkleLayers: [TwinkleLayer]! = []

let upperBound: UInt32 = 10
let lowerBound: UInt32 = 5
let count: UInt = UInt(arc4random_uniform(upperBound) + lowerBound)

for i in 0..<count {
let twinkleLayer: TwinkleLayer = TwinkleLayer()
let x: Int = Int(arc4random_uniform(UInt32(self.layer.bounds.size.width)))
let y: Int = Int(arc4random_uniform(UInt32(self.layer.bounds.size.height)))
twinkleLayer.position = CGPointMake(CGFloat(x), CGFloat(y))
twinkleLayer.opacity = 0
twinkleLayers.append(twinkleLayer)
self.layer.addSublayer(twinkleLayer)

twinkleLayer.addPositionAnimation()
twinkleLayer.addRotationAnimation()
twinkleLayer.addFadeInOutAnimation( CACurrentMediaTime() + CFTimeInterval(0.15 * Float(i)) )
}

twinkleLayers.removeAll(keepCapacity: false)
}

}
@@ -0,0 +1,14 @@
Pod::Spec.new do |s|
s.name = 'Twinkle'
s.version = '0.0.1'
s.license = 'MIT'
s.summary = 'a Swift and easy way to make elements in your iOS app twinkle''
s.homepage = 'https://github.com/piemonte/twinkle'
s.social_media_url = 'http://twitter.com/piemonte'
s.authors = { 'patrick piemonte' => "piemonte@alumni.cmu.edu" }
s.source = { :git => 'https://github.com/piemonte/twinkle.git', :tag => s.version }
s.ios.deployment_target = '8.0'
s.source_files = 'Source/*.swift'
s.resources = 'Source/Resources/*.png'
s.requires_arc = true
end

0 comments on commit 19afe48

Please sign in to comment.
You can’t perform that action at this time.