├── 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 | ![](https://raw.githubusercontent.com/dasdom/InteractivePlayground/master/gifs/push_view_controller.gif) 8 | 9 | ## Animating appearance of buttons 10 | 11 | ![](https://raw.githubusercontent.com/dasdom/InteractivePlayground/master/gifs/animated_buttons.gif) 12 | 13 | ## Using gesture recognizer 14 | 15 | ![](https://raw.githubusercontent.com/dasdom/InteractivePlayground/master/gifs/gesture_recognizer.gif) 16 | 17 | ## Text input 18 | 19 | ![](https://github.com/dasdom/InteractivePlayground/blob/master/gifs/text_input.gif) 20 | 21 | ## Using SpriteKit 22 | 23 | ![](https://raw.githubusercontent.com/dasdom/InteractivePlayground/master/gifs/spritekit.gif) 24 | 25 | ## Spring animation 26 | 27 | ![](https://raw.githubusercontent.com/dasdom/InteractivePlayground/master/gifs/spring_animation.gif) 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 --------------------------------------------------------------------------------