├── InteractivePlayground.playground
├── Pages
│ ├── Animated Buttons.xcplaygroundpage
│ │ ├── Contents.swift
│ │ └── timeline.xctimeline
│ ├── Gesture Recognizer.xcplaygroundpage
│ │ └── Contents.swift
│ ├── Push View Controller.xcplaygroundpage
│ │ ├── Contents.swift
│ │ └── timeline.xctimeline
│ ├── Responder Chain.xcplaygroundpage
│ │ └── Contents.swift
│ ├── Spring Animation.xcplaygroundpage
│ │ ├── Contents.swift
│ │ └── timeline.xctimeline
│ ├── SpriteKit.xcplaygroundpage
│ │ ├── Contents.swift
│ │ └── timeline.xctimeline
│ └── Text Input.xcplaygroundpage
│ │ ├── Contents.swift
│ │ └── timeline.xctimeline
├── contents.xcplayground
└── playground.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ └── dom.xcuserdatad
│ └── UserInterfaceState.xcuserstate
├── LICENCE
├── README.md
└── gifs
├── animated_buttons.gif
├── gesture_recognizer.gif
├── push_view_controller.gif
├── spring_animation.gif
├── spritekit.gif
└── text_input.gif
/InteractivePlayground.playground/Pages/Animated Buttons.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | //: [Previous](@previous)
2 |
3 | import UIKit
4 | import PlaygroundSupport
5 |
6 | class ViewController: UIViewController {
7 |
8 | var allButtons: [UIButton]?
9 |
10 | override func viewDidLoad() {
11 | view.backgroundColor = UIColor.white
12 | view.tintColor = UIColor.white
13 |
14 | navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Show/Hide", style: .plain, target: self, action: #selector(ViewController.animate))
15 | }
16 |
17 | func animate() {
18 | if let allButtons = allButtons {
19 | hide(buttons: allButtons)
20 | } else {
21 | showButtons()
22 | }
23 | }
24 |
25 | func showButtons() {
26 | let makeButtonWithTitle = { (title: String) -> UIButton in
27 | let button = UIButton(type: .system)
28 | button.backgroundColor = UIColor.brown
29 | button.setTitle(title, for: .normal)
30 | button.layer.cornerRadius = 5
31 | return button
32 | }
33 |
34 | let aButton = makeButtonWithTitle("Option a")
35 | let bButton = makeButtonWithTitle("Option b")
36 | let cButton = makeButtonWithTitle("Option c")
37 |
38 | allButtons = [aButton, bButton, cButton]
39 |
40 | guard let allButtons = allButtons else { fatalError() }
41 |
42 | for button in allButtons.reversed() {
43 | button.frame = CGRect(x: 10, y: -40, width: view.frame.size.width-20, height: 40)
44 | view.addSubview(button)
45 | }
46 |
47 | for (index, button) in allButtons.enumerated() {
48 |
49 | UIView.animate(withDuration: 0.5, delay: 0.05 * Double(index), usingSpringWithDamping: 0.7, initialSpringVelocity: 0.0, options: [], animations: {
50 |
51 | button.frame.origin.y = 60 + CGFloat(index) * (button.frame.size.height + 10)
52 | }) { (_) in
53 |
54 | }
55 | }
56 | }
57 |
58 | func hide(buttons: [UIButton]) {
59 | for button in buttons {
60 | UIView.animate(withDuration: 0.5, animations: {
61 | button.frame.origin.y = -40
62 | button.alpha = 0
63 | }, completion: { (_) in
64 | button.removeFromSuperview()
65 | })
66 | }
67 | self.allButtons = nil
68 | }
69 | }
70 |
71 | let navigationController = UINavigationController(rootViewController: ViewController())
72 |
73 | navigationController.view.frame.size = CGSize(width: 320, height: 400)
74 | navigationController.navigationBar.barTintColor = UIColor.white
75 |
76 | PlaygroundPage.current.liveView = navigationController.view
77 |
78 | //: [Next](@next)
79 |
--------------------------------------------------------------------------------
/InteractivePlayground.playground/Pages/Animated Buttons.xcplaygroundpage/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/InteractivePlayground.playground/Pages/Gesture Recognizer.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | //: [Previous](@previous)
2 |
3 | import UIKit
4 | import PlaygroundSupport
5 |
6 | class View: UIView {
7 |
8 | let label: UILabel
9 |
10 | override init(frame: CGRect) {
11 |
12 | label = UILabel()
13 | label.translatesAutoresizingMaskIntoConstraints = false
14 | label.font = UIFont.systemFont(ofSize: 20)
15 | label.text = "Do something"
16 | label.numberOfLines = 0
17 | label.textAlignment = .center
18 |
19 | super.init(frame: frame)
20 |
21 | addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tap(sender:))))
22 |
23 | let leftSwipeRecogniser = UISwipeGestureRecognizer(target: self, action: #selector(swipe(sender:)))
24 | leftSwipeRecogniser.direction = UISwipeGestureRecognizerDirection.left
25 | addGestureRecognizer(leftSwipeRecogniser)
26 |
27 | let rightSwipeRecogniser = UISwipeGestureRecognizer(target: self, action: #selector(swipe(sender:)))
28 | rightSwipeRecogniser.direction = UISwipeGestureRecognizerDirection.right
29 | addGestureRecognizer(rightSwipeRecogniser)
30 |
31 | let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(pan(sender:)))
32 | panRecognizer.require(toFail: leftSwipeRecogniser)
33 | panRecognizer.require(toFail: rightSwipeRecogniser)
34 | addGestureRecognizer(panRecognizer)
35 |
36 | backgroundColor = UIColor.white
37 |
38 | addSubview(label)
39 |
40 | label.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
41 | label.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
42 | label.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
43 | }
44 |
45 | required init?(coder aDecoder: NSCoder) {
46 | fatalError("init(coder:) has not been implemented")
47 | }
48 |
49 | func tap(sender: UITapGestureRecognizer) {
50 | label.text = "tap"
51 | }
52 |
53 | func swipe(sender: UISwipeGestureRecognizer) {
54 | let directionString: String
55 | switch sender.direction {
56 | case UISwipeGestureRecognizerDirection.left:
57 | directionString = "left"
58 | case UISwipeGestureRecognizerDirection.up:
59 | directionString = "up"
60 | case UISwipeGestureRecognizerDirection.right:
61 | directionString = "right"
62 | case UISwipeGestureRecognizerDirection.down:
63 | directionString = "down"
64 | default:
65 | directionString = "w00t?"
66 | }
67 |
68 | label.text = "swipe \(directionString)"
69 | }
70 |
71 | func pan(sender: UIPanGestureRecognizer) {
72 | label.text = "pan with velocity:\nx: \(sender.velocity(in: self).x)\ny: \(sender.velocity(in: self).y)"
73 | }
74 |
75 | }
76 |
77 | let view = View(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
78 |
79 | PlaygroundPage.current.liveView = view
80 |
81 | //: [Next](@next)
82 |
--------------------------------------------------------------------------------
/InteractivePlayground.playground/Pages/Push View Controller.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | //: Playground - noun: a place where people can play
2 |
3 | import UIKit
4 | import PlaygroundSupport
5 |
6 | class TableViewController: UITableViewController {
7 |
8 | override func viewDidLoad() {
9 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
10 | }
11 |
12 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
13 | return 10
14 | }
15 |
16 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
17 |
18 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath)
19 |
20 | cell.textLabel?.text = "Row: \(indexPath.row)"
21 |
22 | return cell
23 | }
24 |
25 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
26 |
27 | let next = TableViewController()
28 | next.title = "Row: \(indexPath.row)"
29 | navigationController?.pushViewController(next, animated: true)
30 |
31 | print(navigationController?.viewControllers.count ?? "Hö?")
32 | }
33 |
34 | }
35 |
36 | let tableViewController = TableViewController()
37 | tableViewController.title = "start"
38 | let navigationController = UINavigationController(rootViewController: tableViewController)
39 |
40 | navigationController.view.frame.size = CGSize(width: 320, height: 400)
41 | navigationController.navigationBar.barTintColor = UIColor.white
42 |
43 | PlaygroundPage.current.liveView = navigationController.view
44 |
45 |
--------------------------------------------------------------------------------
/InteractivePlayground.playground/Pages/Push View Controller.xcplaygroundpage/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/InteractivePlayground.playground/Pages/Responder Chain.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | //: [Previous](@previous)
2 |
3 | import UIKit
4 | import PlaygroundSupport
5 |
6 | class View: UIView {
7 | let titleTextField: UITextField
8 | let titleLabel: UILabel
9 | let button: UIButton
10 |
11 | override init(frame: CGRect) {
12 | titleTextField = UITextField()
13 | titleTextField.placeholder = "Title"
14 | titleTextField.backgroundColor = UIColor(white: 0.98, alpha: 1.0)
15 |
16 | titleLabel = UILabel()
17 | titleLabel.backgroundColor = UIColor(white: 0.98, alpha: 1.0)
18 | titleLabel.text = " "
19 |
20 | button = UIButton(type: .system)
21 | button.setTitle("Set Text", for: .normal)
22 | button.addTarget(nil, action: #selector(ViewController.setText), for: .touchUpInside)
23 |
24 | super.init(frame: frame)
25 |
26 | backgroundColor = UIColor.white
27 |
28 | let stackView = UIStackView(arrangedSubviews: [titleTextField, titleLabel, button])
29 | stackView.translatesAutoresizingMaskIntoConstraints = false
30 | stackView.axis = .vertical
31 | stackView.distribution = .equalSpacing
32 | stackView.spacing = 10
33 |
34 | addSubview(stackView)
35 |
36 | let views = ["stackView": stackView]
37 | var layoutConstraints = [NSLayoutConstraint]()
38 | layoutConstraints += NSLayoutConstraint.constraints(withVisualFormat: "|-20-[stackView]-20-|", options: [], metrics: nil, views: views)
39 | layoutConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-50-[stackView]", options: [], metrics: nil, views: views)
40 | NSLayoutConstraint.activate(layoutConstraints)
41 | }
42 |
43 | required init?(coder aDecoder: NSCoder) {
44 | fatalError("init(coder:) has not been implemented")
45 | }
46 | }
47 |
48 | class ViewController: UIViewController {
49 |
50 | var contentView: View { return view as! View }
51 |
52 | override func loadView() {
53 | view = View()
54 | }
55 |
56 | func setText() {
57 | contentView.titleLabel.text = contentView.titleTextField.text
58 | }
59 | }
60 |
61 | let viewController = ViewController()
62 | viewController.view.frame = CGRect(x: 0, y: 0, width: 320, height: 400)
63 |
64 | PlaygroundPage.current.liveView = viewController.view
65 |
66 | //: [Next](@next)
67 |
--------------------------------------------------------------------------------
/InteractivePlayground.playground/Pages/Spring Animation.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | //: [Previous](@previous)
2 |
3 | import UIKit
4 | import PlaygroundSupport
5 |
6 | class View: UIView {
7 |
8 | let redBox: UIView
9 | let durationSlider: UISlider
10 | let durationLabel: UILabel
11 | let dampingSlider: UISlider
12 | let dampingLabel: UILabel
13 | let initialVelocitySlider: UISlider
14 | let initialVelocityLabel: UILabel
15 |
16 | override init(frame: CGRect) {
17 |
18 | let makeStackView = { (labelText: String, sliderMin: Float, sliderMax: Float) -> (UILabel, UISlider, UIStackView) in
19 | let slider = UISlider()
20 | slider.minimumValue = sliderMin
21 | slider.maximumValue = sliderMax
22 |
23 | let label = UILabel()
24 | label.text = labelText + ": \(sliderMin)"
25 | label.textAlignment = .center
26 |
27 | let stackView = UIStackView(arrangedSubviews: [label, slider])
28 | stackView.axis = .vertical
29 | stackView.alignment = .fill
30 | return (label, slider, stackView)
31 | }
32 |
33 | var durationStackView: UIStackView = UIStackView()
34 | (durationLabel, durationSlider, durationStackView) = makeStackView("Duration", 0.1, 10)
35 |
36 | var dampingStackView: UIStackView = UIStackView()
37 | (dampingLabel, dampingSlider, dampingStackView) = makeStackView("Damping", 0.1, 1)
38 |
39 | var velocityStackView: UIStackView = UIStackView()
40 | (initialVelocityLabel, initialVelocitySlider, velocityStackView) = makeStackView("Initial Velocity", 0, 5)
41 |
42 | let button = UIButton()
43 | button.setTitle("Animate", for: .normal)
44 | button.backgroundColor = UIColor.blue
45 |
46 | let stackView = UIStackView(arrangedSubviews: [durationStackView, dampingStackView, velocityStackView, button])
47 | stackView.translatesAutoresizingMaskIntoConstraints = false
48 | stackView.axis = .vertical
49 | stackView.spacing = 30
50 |
51 | redBox = UIView(frame: CGRect(x: 50, y: 20, width: 30, height: 30))
52 | redBox.backgroundColor = UIColor.red
53 |
54 | super.init(frame: frame)
55 |
56 | backgroundColor = UIColor.white
57 |
58 | addSubview(redBox)
59 | addSubview(stackView)
60 |
61 | durationSlider.addTarget(self, action: #selector(changeDuration(sender:)), for: .valueChanged)
62 | dampingSlider.addTarget(self, action: #selector(changeDamping(sender:)), for: .valueChanged)
63 | initialVelocitySlider.addTarget(self, action: #selector(changeVelocity(sender:)), for: .valueChanged)
64 | button.addTarget(self, action: #selector(View.animate as (View) -> () -> ()), for: .touchUpInside)
65 |
66 | let views = ["stackView": stackView]
67 | var layoutConstraints = [NSLayoutConstraint]()
68 | layoutConstraints += NSLayoutConstraint.constraints(withVisualFormat: "|-[stackView]-|", options: [], metrics: nil, views: views)
69 | layoutConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-80-[stackView]", options: [], metrics: nil, views: views)
70 | NSLayoutConstraint.activate(layoutConstraints)
71 | }
72 |
73 | required init?(coder aDecoder: NSCoder) {
74 | fatalError("init(coder:) has not been implemented")
75 | }
76 |
77 | func animate() {
78 | UIView.animate(withDuration: Double(durationSlider.value), delay: 0.0, usingSpringWithDamping:CGFloat(dampingSlider.value), initialSpringVelocity: CGFloat(initialVelocitySlider.value), options: [], animations: {
79 | if self.redBox.frame.origin.x > 100 {
80 | self.redBox.frame.origin.x = 50
81 | } else {
82 | self.redBox.frame.origin.x = self.frame.width-self.redBox.frame.size.width-50
83 | }
84 | }) { (_) in
85 |
86 | }
87 | }
88 |
89 | func changeDuration(sender: UISlider) {
90 | durationLabel.text = "Duration: \(durationSlider.value)"
91 | }
92 |
93 | func changeDamping(sender: UISlider) {
94 | dampingLabel.text = "Damping: \(dampingSlider.value)"
95 | }
96 |
97 | func changeVelocity(sender: UISlider) {
98 | initialVelocityLabel.text = "Initial Velocity: \(initialVelocitySlider.value)"
99 | }
100 | }
101 |
102 | let view = View(frame: CGRect(x: 0, y: 0, width: 300, height: 400))
103 |
104 | PlaygroundPage.current.liveView = view
105 |
106 | //: [Next](@next)
107 |
--------------------------------------------------------------------------------
/InteractivePlayground.playground/Pages/Spring Animation.xcplaygroundpage/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/InteractivePlayground.playground/Pages/SpriteKit.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | //: [Previous](@previous)
2 |
3 | import UIKit
4 | import SpriteKit
5 | import PlaygroundSupport
6 |
7 | class Scene: SKScene {
8 |
9 | override init(size: CGSize) {
10 | super.init(size: size)
11 |
12 | physicsBody = SKPhysicsBody(edgeLoopFrom: self.frame)
13 | }
14 |
15 | required init?(coder aDecoder: NSCoder) {
16 | fatalError("init(coder:) has not been implemented")
17 | }
18 |
19 | override func touchesBegan(_ touches: Set, with event: UIEvent?) {
20 |
21 | if let touch = touches.first {
22 |
23 | let sprite = SKSpriteNode(color: UIColor.red, size: CGSize(width: 30, height: 30))
24 |
25 | let physicsBody = SKPhysicsBody(rectangleOf: sprite.size)
26 | // physicsBody.restitution = 0.5
27 | sprite.physicsBody = physicsBody
28 |
29 | addChild(sprite)
30 | sprite.position = touch.location(in: self)
31 | }
32 | }
33 |
34 | }
35 |
36 | let skView = SKView(frame: CGRect(x: 0, y: 0, width: 300, height: 400))
37 | skView.showsNodeCount = true
38 | skView.showsFPS = true
39 | let scene = Scene(size: CGSize(width: 300, height: 400))
40 |
41 | skView.presentScene(scene)
42 |
43 | PlaygroundPage.current.liveView = skView
44 |
45 | //: [Next](@next)
46 |
--------------------------------------------------------------------------------
/InteractivePlayground.playground/Pages/SpriteKit.xcplaygroundpage/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/InteractivePlayground.playground/Pages/Text Input.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | //: [Previous](@previous)
2 |
3 | import UIKit
4 | import PlaygroundSupport
5 |
6 | class View: UIView {
7 |
8 | let textField: UITextField
9 | let label: UILabel
10 |
11 | override init(frame: CGRect) {
12 |
13 | textField = UITextField()
14 | textField.backgroundColor = UIColor.yellow
15 | textField.placeholder = "Type something"
16 |
17 | label = UILabel()
18 | label.backgroundColor = UIColor.black
19 | label.textColor = UIColor.white
20 | label.text = " "
21 |
22 | let button = UIButton(type: .system)
23 | button.setTitle("I'm a button!", for: .normal)
24 | button.tintColor = UIColor.white
25 |
26 | let stackView = UIStackView(arrangedSubviews: [textField, label, button])
27 | stackView.translatesAutoresizingMaskIntoConstraints = false
28 | stackView.axis = .vertical
29 | stackView.distribution = .equalSpacing
30 | stackView.spacing = 10
31 |
32 | super.init(frame: frame)
33 |
34 | backgroundColor = UIColor.brown
35 |
36 | addSubview(stackView)
37 | button.addTarget(self, action: #selector(View.changeLabelText), for: .touchUpInside)
38 |
39 | let views = ["stackView": stackView]
40 | var layoutConstraints = [NSLayoutConstraint]()
41 | layoutConstraints += NSLayoutConstraint.constraints(withVisualFormat: "|-[stackView]-|", options: [], metrics: nil, views: views)
42 | layoutConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-[stackView]", options: [], metrics: nil, views: views)
43 | NSLayoutConstraint.activate(layoutConstraints)
44 | }
45 |
46 | required init?(coder aDecoder: NSCoder) {
47 | fatalError("init(coder:) has not been implemented")
48 | }
49 |
50 | func changeLabelText() {
51 | label.text = textField.text
52 | }
53 | }
54 |
55 | let hostView = View(frame: CGRect(x: 0, y: 0, width: 320, height: 200))
56 |
57 | PlaygroundPage.current.liveView = hostView
58 |
59 | //: [Next](@next)
60 |
--------------------------------------------------------------------------------
/InteractivePlayground.playground/Pages/Text Input.xcplaygroundpage/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/InteractivePlayground.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/InteractivePlayground.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/InteractivePlayground.playground/playground.xcworkspace/xcuserdata/dom.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dasdom/InteractivePlayground/10d5848a67733565e81be9e78ed9dbaa9e8e47f5/InteractivePlayground.playground/playground.xcworkspace/xcuserdata/dom.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Dominik Hauser
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # InteractivePlayground
2 |
3 | Exploring interactivity in Playgrounds (new in Xcode 7.3)
4 |
5 | ## Pushing a view controller
6 |
7 | 
8 |
9 | ## Animating appearance of buttons
10 |
11 | 
12 |
13 | ## Using gesture recognizer
14 |
15 | 
16 |
17 | ## Text input
18 |
19 | 
20 |
21 | ## Using SpriteKit
22 |
23 | 
24 |
25 | ## Spring animation
26 |
27 | 
28 |
--------------------------------------------------------------------------------
/gifs/animated_buttons.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dasdom/InteractivePlayground/10d5848a67733565e81be9e78ed9dbaa9e8e47f5/gifs/animated_buttons.gif
--------------------------------------------------------------------------------
/gifs/gesture_recognizer.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dasdom/InteractivePlayground/10d5848a67733565e81be9e78ed9dbaa9e8e47f5/gifs/gesture_recognizer.gif
--------------------------------------------------------------------------------
/gifs/push_view_controller.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dasdom/InteractivePlayground/10d5848a67733565e81be9e78ed9dbaa9e8e47f5/gifs/push_view_controller.gif
--------------------------------------------------------------------------------
/gifs/spring_animation.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dasdom/InteractivePlayground/10d5848a67733565e81be9e78ed9dbaa9e8e47f5/gifs/spring_animation.gif
--------------------------------------------------------------------------------
/gifs/spritekit.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dasdom/InteractivePlayground/10d5848a67733565e81be9e78ed9dbaa9e8e47f5/gifs/spritekit.gif
--------------------------------------------------------------------------------
/gifs/text_input.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dasdom/InteractivePlayground/10d5848a67733565e81be9e78ed9dbaa9e8e47f5/gifs/text_input.gif
--------------------------------------------------------------------------------