├── Resources
├── AnimatorCover.png
└── AnimatorDemoExample.gif
├── Animator Demo.playground
├── timeline.xctimeline
├── contents.xcplayground
├── playground.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── Vishal.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
├── Contents.swift
└── Sources
│ └── Animator.swift
├── LICENSE
├── README.md
└── Source
└── Animator.swift
/Resources/AnimatorCover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vishalvshekkar/Animator/HEAD/Resources/AnimatorCover.png
--------------------------------------------------------------------------------
/Resources/AnimatorDemoExample.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vishalvshekkar/Animator/HEAD/Resources/AnimatorDemoExample.gif
--------------------------------------------------------------------------------
/Animator Demo.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Animator Demo.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Animator Demo.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Animator Demo.playground/playground.xcworkspace/xcuserdata/Vishal.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vishalvshekkar/Animator/HEAD/Animator Demo.playground/playground.xcworkspace/xcuserdata/Vishal.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Vishal V. Shekkar
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Animator Demo.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | //: Playground - noun: a place where people can play
2 |
3 | import UIKit
4 | import PlaygroundSupport
5 |
6 | let containerView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 400, height: 400))
7 | containerView.backgroundColor = UIColor.black
8 | PlaygroundPage.current.liveView = containerView
9 |
10 | let smallView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 20, height: 20))
11 | smallView.backgroundColor = UIColor.cyan
12 | containerView.addSubview(smallView)
13 |
14 | let smallView3 = UIView(frame: CGRect(x: 380, y: 380, width: 20, height: 20))
15 | smallView3.backgroundColor = UIColor.red
16 | containerView.addSubview(smallView3)
17 |
18 | Animator.addAnimations() {
19 | smallView.frame.origin = CGPoint(x: 380, y: 0)
20 | smallView3.frame.origin = CGPoint(x: 0, y: 380)
21 | }.addAnimations() {
22 | smallView.frame.origin = CGPoint(x: 380, y: 380)
23 | smallView3.frame.origin = CGPoint(x: 0, y: 0)
24 | }.addAnimations(usingSpringWithDamping: 0.7, initialSpringVelocity: 0) {
25 | smallView.frame.origin = CGPoint(x: 190, y: 190)
26 | smallView3.frame.origin = CGPoint(x: 190, y: 190)
27 | }.addAnimations(usingSpringWithDamping: 0.7, initialSpringVelocity: 0) {
28 | smallView.frame.origin = CGPoint(x: 380, y: 0)
29 | smallView3.frame.origin = CGPoint(x: 0, y: 380)
30 | }.addAnimations(usingSpringWithDamping: 0.7, initialSpringVelocity: 0) {
31 | smallView.frame.origin = CGPoint(x: 380, y: 380)
32 | smallView3.frame.origin = CGPoint(x: 0, y: 0)
33 | }.addAnimations(withDuration: 1, delay: 1, options: [UIViewAnimationOptions.curveLinear], animations: {
34 | smallView.frame.origin = CGPoint(x: 260, y: 380)
35 | smallView3.frame.size = CGSize(width: 80, height: 80)
36 | }).animate {
37 | print("Completed")
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Animator
4 | Animator is a block-based `UIView` animation helper which enables complex animations to be performed with ease.
5 |
6 | When there's a complex multi-step animation that needs to be performed, we usually break these down into smaller animatable steps and animate these smaller steps one after the other. To do this, every new animation step needs to be invoked after the completion of the last step. When using the stock `UIView` `animate` methods, the code gets very ugly and unmanageable with a complex animation. If you add every step into a method and invoke these methods in the completion blocks of the last animation step, the code readability reduces slightly as there's no continuation.
7 |
8 | As a developer, when you visualize an animation, and when you need to prototype that and polish over it, having a readable codebase helps a lot. It helps you better understand the steps and fix bugs easily. That's where `Animator` comes in to help.
9 |
10 | #Using `Animator`
11 |
12 | Most simply, call `addAnimations:` with a closure that contains the animation code on `Animator`. Invoke `animate` to begin animating. This animates whatever statements are in the block with a 1s duration (default).
13 |
14 | ```
15 | Animator.addAnimations() {
16 | //Animation code
17 | }.animate()
18 | ```
19 |
20 | 1. Every block of animations that need to be performed during one animation cycle needs to be passed as a parameter to `addAnimations:`
21 | 2. `addAnimations:` can be chained.
22 | 3. The blocks passed with `addAnimations:` are executed in the ordered these methods are invoked.
23 | 4. Every animation block is performed only after the previous block completes.
24 | 5. Call `animate` after all animations have been added to begin animating from the first block.
25 | 6. `addAnimations:` method contains various parameters that can be passed along to tweak the animation behavior.
26 | 7. Most of these parameters have been given default values to get you started with ease.
27 |
28 | Many animation blocks can be chained together with very simple and easy-on-the-eye syntax as seen below.
29 |
30 | ```
31 | Animator.addAnimations() {
32 | //Animation code
33 | }.addAnimations() {
34 | //Animation code
35 | }.addAnimations() {
36 | //Animation code
37 | }.addAnimations() {
38 | //Animation code
39 | }.addAnimations() {
40 | //Animation code
41 | }.animate()
42 | ```
43 | Each block is executed after the completion of the last block.
44 |
45 | #Features of `Animator`
46 |
47 | 1. Supports setting duration, delay and animationOptions.
48 |
49 | ```
50 | Animator.addAnimations(withDuration: 1, delay: 1, options: [UIViewAnimationOptions.curveLinear]) {
51 | //Animation code
52 | }
53 | ```
54 |
55 | 2. Supports animations with spring damping and initial velocity constants.
56 |
57 | ```
58 | Animator.addAnimations(usingSpringWithDamping: 0.7, initialSpringVelocity: 0) {
59 | //Animation code
60 | }
61 | ```
62 |
63 | 3. You could pass duration, delay and animationOptions along with with spring damping and initial velocity constants to the function. You could instead choose to leave out duration or animatiOptions or delay selectively when invoking the function.
64 |
65 | 4. The `animate` method can accept a completion block to let you know when the entire batch of animations that have been chained together completes.
66 |
67 | ```
68 | Animator.addAnimations() {
69 | //Animation code
70 | }.addAnimations() {
71 | //Animation code
72 | }.addAnimations() {
73 | //Animation code
74 | }.animate {
75 | //Completion of all animations chained above
76 | }
77 | ```
78 | 
79 |
80 | #Other Facts
81 |
82 | 1. Developed on Swift 3.0.1
83 | 2. The playground contains a demo which you can go through.
84 | 3. You need to open the assistant editor in the Playground to view the output animation. (View -> Assistant Editor -> Show Assistant Editor) or the keyboard shortcut option+command+return (if you haven't changed that).
85 | 4. To use `Animator` in your project, just drag the file 'Animator.swift' into your project which is available in the 'Source' directory in this repository. (Copy it to your project directory)
86 |
--------------------------------------------------------------------------------
/Source/Animator.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | public class Animator {
4 |
5 | public typealias AnimatorAnimationsClosure = () -> ()
6 | public typealias AnimatorCompletionClosure = () -> ()
7 |
8 | struct AnimationParameters {
9 |
10 | enum Animationtype {
11 | case withSpringDamping
12 | case withoutSpringDamping
13 | }
14 |
15 | let duration: TimeInterval
16 | let delay: TimeInterval
17 | let options: UIViewAnimationOptions
18 | let animationtype: Animationtype
19 | let damping: CGFloat?
20 | let initialVelocity: CGFloat?
21 |
22 | init(duration: TimeInterval, delay: TimeInterval, options: UIViewAnimationOptions, animationtype: Animationtype) {
23 | self.duration = duration
24 | self.delay = delay
25 | self.options = options
26 | self.animationtype = animationtype
27 | self.damping = nil
28 | self.initialVelocity = nil
29 | }
30 |
31 | init(duration: TimeInterval, delay: TimeInterval, options: UIViewAnimationOptions, animationtype: Animationtype, damping: CGFloat, initialVelocity: CGFloat) {
32 | self.duration = duration
33 | self.delay = delay
34 | self.options = options
35 | self.animationtype = animationtype
36 | self.damping = damping
37 | self.initialVelocity = initialVelocity
38 | }
39 | }
40 |
41 | private var animations = [(parameters: AnimationParameters, animationClosures: AnimatorAnimationsClosure)]()
42 |
43 | public static func addAnimations(withDuration: TimeInterval = 1, delay: TimeInterval? = nil, options: UIViewAnimationOptions = [], animations: @escaping AnimatorAnimationsClosure) -> Animator {
44 | let newAnimator = Animator()
45 | let parameters = AnimationParameters(duration: withDuration, delay: delay ?? 0, options: options, animationtype: .withoutSpringDamping)
46 | newAnimator.animations.append((parameters, animations))
47 | return newAnimator
48 | }
49 |
50 | public static func addAnimations(withDuration: TimeInterval = 1, delay: TimeInterval? = nil, usingSpringWithDamping: CGFloat, initialSpringVelocity: CGFloat, options: UIViewAnimationOptions = [], animations: @escaping AnimatorAnimationsClosure) -> Animator {
51 | let newAnimator = Animator()
52 | let parameters = AnimationParameters(duration: withDuration, delay: delay ?? 0, options: options, animationtype: .withSpringDamping, damping: usingSpringWithDamping, initialVelocity: initialSpringVelocity)
53 | newAnimator.animations.append((parameters, animations))
54 | return newAnimator
55 | }
56 |
57 | public func addAnimations(withDuration: TimeInterval = 1, delay: TimeInterval? = nil, options: UIViewAnimationOptions = [], animations: @escaping AnimatorAnimationsClosure) -> Animator {
58 | let parameters = AnimationParameters(duration: withDuration, delay: delay ?? 0, options: options,animationtype: .withoutSpringDamping)
59 | self.animations.append((parameters, animations))
60 | return self
61 | }
62 |
63 | public func addAnimations(withDuration: TimeInterval = 1, delay: TimeInterval? = nil, usingSpringWithDamping: CGFloat, initialSpringVelocity: CGFloat, options: UIViewAnimationOptions = [], animations: @escaping AnimatorAnimationsClosure) -> Animator {
64 | let parameters = AnimationParameters(duration: withDuration, delay: delay ?? 0, options: options, animationtype: .withSpringDamping, damping: usingSpringWithDamping, initialVelocity: initialSpringVelocity)
65 | self.animations.append((parameters, animations))
66 | return self
67 | }
68 |
69 | public func animate(completion: AnimatorCompletionClosure? = nil) {
70 | if let closureToAnimate = animations.first {
71 | let parameters = closureToAnimate.parameters
72 | if parameters.animationtype == .withoutSpringDamping {
73 | UIView.animate(withDuration: parameters.duration, delay: parameters.delay, options: parameters.options, animations: closureToAnimate.animationClosures, completion: { (completed) in
74 | self.animations.removeFirst()
75 | self.animate(completion: completion)
76 | })
77 | } else {
78 | UIView.animate(withDuration: parameters.duration, delay: parameters.delay, usingSpringWithDamping: parameters.damping ?? 0, initialSpringVelocity: parameters.initialVelocity ?? 0, options: parameters.options, animations: closureToAnimate.animationClosures, completion: { (completed) in
79 | self.animations.removeFirst()
80 | self.animate(completion: completion)
81 | })
82 | }
83 | } else {
84 | completion?()
85 | }
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/Animator Demo.playground/Sources/Animator.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | public class Animator {
4 |
5 | public typealias AnimatorAnimationsClosure = () -> ()
6 | public typealias AnimatorCompletionClosure = () -> ()
7 |
8 | struct AnimationParameters {
9 |
10 | enum Animationtype {
11 | case withSpringDamping
12 | case withoutSpringDamping
13 | }
14 |
15 | let duration: TimeInterval
16 | let delay: TimeInterval
17 | let options: UIViewAnimationOptions
18 | let animationtype: Animationtype
19 | let damping: CGFloat?
20 | let initialVelocity: CGFloat?
21 |
22 | init(duration: TimeInterval, delay: TimeInterval, options: UIViewAnimationOptions, animationtype: Animationtype) {
23 | self.duration = duration
24 | self.delay = delay
25 | self.options = options
26 | self.animationtype = animationtype
27 | self.damping = nil
28 | self.initialVelocity = nil
29 | }
30 |
31 | init(duration: TimeInterval, delay: TimeInterval, options: UIViewAnimationOptions, animationtype: Animationtype, damping: CGFloat, initialVelocity: CGFloat) {
32 | self.duration = duration
33 | self.delay = delay
34 | self.options = options
35 | self.animationtype = animationtype
36 | self.damping = damping
37 | self.initialVelocity = initialVelocity
38 | }
39 | }
40 |
41 | private var animations = [(parameters: AnimationParameters, animationClosures: AnimatorAnimationsClosure)]()
42 |
43 | public static func addAnimations(withDuration: TimeInterval = 1, delay: TimeInterval? = nil, options: UIViewAnimationOptions = [], animations: @escaping AnimatorAnimationsClosure) -> Animator {
44 | let newAnimator = Animator()
45 | let parameters = AnimationParameters(duration: withDuration, delay: delay ?? 0, options: options, animationtype: .withoutSpringDamping)
46 | newAnimator.animations.append((parameters, animations))
47 | return newAnimator
48 | }
49 |
50 | public static func addAnimations(withDuration: TimeInterval = 1, delay: TimeInterval? = nil, usingSpringWithDamping: CGFloat, initialSpringVelocity: CGFloat, options: UIViewAnimationOptions = [], animations: @escaping AnimatorAnimationsClosure) -> Animator {
51 | let newAnimator = Animator()
52 | let parameters = AnimationParameters(duration: withDuration, delay: delay ?? 0, options: options, animationtype: .withSpringDamping, damping: usingSpringWithDamping, initialVelocity: initialSpringVelocity)
53 | newAnimator.animations.append((parameters, animations))
54 | return newAnimator
55 | }
56 |
57 | public func addAnimations(withDuration: TimeInterval = 1, delay: TimeInterval? = nil, options: UIViewAnimationOptions = [], animations: @escaping AnimatorAnimationsClosure) -> Animator {
58 | let parameters = AnimationParameters(duration: withDuration, delay: delay ?? 0, options: options,animationtype: .withoutSpringDamping)
59 | self.animations.append((parameters, animations))
60 | return self
61 | }
62 |
63 | public func addAnimations(withDuration: TimeInterval = 1, delay: TimeInterval? = nil, usingSpringWithDamping: CGFloat, initialSpringVelocity: CGFloat, options: UIViewAnimationOptions = [], animations: @escaping AnimatorAnimationsClosure) -> Animator {
64 | let parameters = AnimationParameters(duration: withDuration, delay: delay ?? 0, options: options, animationtype: .withSpringDamping, damping: usingSpringWithDamping, initialVelocity: initialSpringVelocity)
65 | self.animations.append((parameters, animations))
66 | return self
67 | }
68 |
69 | public func animate(completion: AnimatorCompletionClosure? = nil) {
70 | if let closureToAnimate = animations.first {
71 | let parameters = closureToAnimate.parameters
72 | if parameters.animationtype == .withoutSpringDamping {
73 | UIView.animate(withDuration: parameters.duration, delay: parameters.delay, options: parameters.options, animations: closureToAnimate.animationClosures, completion: { (completed) in
74 | self.animations.removeFirst()
75 | self.animate(completion: completion)
76 | })
77 | } else {
78 | UIView.animate(withDuration: parameters.duration, delay: parameters.delay, usingSpringWithDamping: parameters.damping ?? 0, initialSpringVelocity: parameters.initialVelocity ?? 0, options: parameters.options, animations: closureToAnimate.animationClosures, completion: { (completed) in
79 | self.animations.removeFirst()
80 | self.animate(completion: completion)
81 | })
82 | }
83 | } else {
84 | completion?()
85 | }
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------