Full Code of MengTo/Spring for AI

master 50d92a5b9e08 cached
53 files
242.6 KB
63.2k tokens
1 requests
Download .txt
Showing preview only (258K chars total). Download the full file or copy to clipboard to get everything.
Repository: MengTo/Spring
Branch: master
Commit: 50d92a5b9e08
Files: 53
Total size: 242.6 KB

Directory structure:
gitextract_6f9a0zjb/

├── .gitignore
├── .swift-version
├── LICENSE
├── README.md
├── Spring/
│   ├── AsyncButton.swift
│   ├── AsyncImageView.swift
│   ├── AutoTextView.swift
│   ├── BlurView.swift
│   ├── DesignableButton.swift
│   ├── DesignableImageView.swift
│   ├── DesignableLabel.swift
│   ├── DesignableTabBarController.swift
│   ├── DesignableTextField.swift
│   ├── DesignableTextView.swift
│   ├── DesignableView.swift
│   ├── ImageLoader.swift
│   ├── Info.plist
│   ├── KeyboardLayoutConstraint.swift
│   ├── LoadingView.swift
│   ├── LoadingView.xib
│   ├── Misc.swift
│   ├── SoundPlayer.swift
│   ├── Spring.h
│   ├── Spring.swift
│   ├── SpringAnimation.swift
│   ├── SpringButton.swift
│   ├── SpringImageView.swift
│   ├── SpringLabel.swift
│   ├── SpringTextField.swift
│   ├── SpringTextView.swift
│   ├── SpringView.swift
│   ├── TransitionManager.swift
│   ├── TransitionZoom.swift
│   └── UnwindSegue.swift
├── Spring.podspec
├── SpringApp/
│   ├── AppDelegate.swift
│   ├── Base.lproj/
│   │   ├── LaunchScreen.xib
│   │   └── Main.storyboard
│   ├── CodeViewController.swift
│   ├── Images.xcassets/
│   │   ├── AppIcon.appiconset/
│   │   │   └── Contents.json
│   │   ├── icon-shape.imageset/
│   │   │   └── Contents.json
│   │   └── loading.imageset/
│   │       └── Contents.json
│   ├── Info.plist
│   ├── OptionsViewController.swift
│   └── SpringViewController.swift
├── SpringApp.xcodeproj/
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   └── contents.xcworkspacedata
│   └── xcshareddata/
│       └── xcschemes/
│           ├── Spring.xcscheme
│           └── SpringApp.xcscheme
├── SpringAppTests/
│   ├── Info.plist
│   └── SpringAppTests.swift
└── SpringTests/
    ├── Info.plist
    └── SpringTests.swift

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
.DS_Store


================================================
FILE: .swift-version
================================================
4


================================================
FILE: LICENSE
================================================
Copyright (c) 2014-2015 Meng To (meng@designcode.io)

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.


================================================
FILE: README.md
================================================
## Updated for Swift 4.2
Requires Xcode 10 and Swift 4.2.

## Installation
Drop in the Spring folder to your Xcode project (make sure to enable "Copy items if needed" and "Create groups").

Or via CocoaPods:
```
use_frameworks!
pod 'Spring', :git => 'https://github.com/MengTo/Spring.git'
```

## Usage with Storyboard
In Identity Inspector, connect the UIView to SpringView Class and set the animation properties in Attribute Inspector.

![](http://cl.ly/image/241o0G1G3S36/download/springsetup.jpg)

## Usage with Code
    layer.animation = "squeezeDown"
    layer.animate()

## Demo The Animations
![](http://cl.ly/image/1n1E2j3W3y24/springscreen.jpg)

## Chaining Animations
    layer.y = -50
    animateToNext {
      layer.animation = "fall"
      layer.animateTo()
    }

## Functions
    animate()
    animateNext { ... }
    animateTo()
    animateToNext { ... }

## Animation
    shake
    pop
    morph
    squeeze
    wobble
    swing
    flipX
    flipY
    fall
    squeezeLeft
    squeezeRight
    squeezeDown
    squeezeUp
    slideLeft
    slideRight
    slideDown
    slideUp
    fadeIn
    fadeOut
    fadeInLeft
    fadeInRight
    fadeInDown
    fadeInUp
    zoomIn
    zoomOut
    flash

## Curve
    spring
    linear
    easeIn
    easeOut
    easeInOut

## Properties
    force
    duration
    delay
    damping
    velocity
    repeatCount
    scale
    x
    y
    rotate

\* Not all properties work together. Play with the demo app.


## Autostart
Allows you to animate without code. Don't need to use this if you plan to start the animation in code.

## Autohide
Saves you the hassle of adding a line "layer.alpha = 0" in viewDidLoad().

## Known issue
Animations won't autostart when view is reached via performSegueWithIdentifier.

## Tutorials
- Tutorials available on [Design+Code](https://designcode.io/swiftapp).
- [Integrate Spring to existing Objective-C projects](https://medium.com/ios-apprentice/using-swift-in-objective-c-projects-f7e7a09f8be)

## ChangeLog
- At [ChangeLog](https://github.com/MengTo/Spring/wiki/CHANGELOG) wiki page

## License

Spring is released under the MIT license. See LICENSE for details.


================================================
FILE: Spring/AsyncButton.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 James Tang (j@jamztang.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

public class AsyncButton: UIButton {
    
    private var imageURL = [UInt:NSURL]()
    private var placeholderImage = [UInt:UIImage]()
    
    
    public func setImageURL(url: NSURL?, placeholderImage placeholder:UIImage?, forState state:UIControl.State) {
        
        imageURL[state.rawValue] = url
        placeholderImage[state.rawValue] = placeholder
        
        if let urlString = url?.absoluteString {
            ImageLoader.sharedLoader.imageForUrl(urlString: urlString) { [weak self] image, url in
                
                if let strongSelf = self {
                    
                    DispatchQueue.main.async(execute: { () -> Void in
                        if strongSelf.imageURL[state.rawValue]?.absoluteString == url {
                            strongSelf.setImage(image, for: state)
                        }
                    })
                }
            }
        }
    }
    
}


================================================
FILE: Spring/AsyncImageView.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 James Tang (j@jamztang.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

public class AsyncImageView: UIImageView {

    public var placeholderImage : UIImage?

    public var url : NSURL? {
        didSet {
            self.image = placeholderImage
            if let urlString = url?.absoluteString {
                ImageLoader.sharedLoader.imageForUrl(urlString: urlString) { [weak self] image, url in
                    if let strongSelf = self {
                        DispatchQueue.main.async(execute: { () -> Void in
                            if strongSelf.url?.absoluteString == url {
                                strongSelf.image = image ?? strongSelf.placeholderImage
                            }
                        })
                    }
                }
            }
        }
    }

    public func setURL(url: NSURL?, placeholderImage: UIImage?) {
        self.placeholderImage = placeholderImage
        self.url = url
    }

}


================================================
FILE: Spring/AutoTextView.swift
================================================
//
//  AutoTextView.swift
//  SpringApp
//
//  Created by Meng To on 2015-03-27.
//  Copyright (c) 2015 Meng To. All rights reserved.
//

import UIKit

public class AutoTextView: UITextView {

    public override var intrinsicContentSize: CGSize {
        get {
            var size = self.sizeThatFits(CGSize(width: self.frame.size.width, height: CGFloat.greatestFiniteMagnitude))
            size.width = self.frame.size.width
            if text.length == 0 {
                size.height = 0
            }
            
            contentInset = UIEdgeInsets(top: -4, left: -4, bottom: -4, right: -4)
            layoutIfNeeded()
            
            return size
        }
    }
}


================================================
FILE: Spring/BlurView.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

public func insertBlurView (view: UIView, style: UIBlurEffect.Style) -> UIVisualEffectView {
    view.backgroundColor = UIColor.clear
    
    let blurEffect = UIBlurEffect(style: style)
    let blurEffectView = UIVisualEffectView(effect: blurEffect)
    blurEffectView.frame = view.bounds
    view.insertSubview(blurEffectView, at: 0)
    return blurEffectView
}


================================================
FILE: Spring/DesignableButton.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

@IBDesignable public class DesignableButton: SpringButton {

    @IBInspectable public var borderColor: UIColor = UIColor.clear {
        didSet {
            layer.borderColor = borderColor.cgColor
        }
    }
    
    @IBInspectable public var borderWidth: CGFloat = 0 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }
    
    @IBInspectable public var cornerRadius: CGFloat = 0 {
        didSet {
            layer.cornerRadius = cornerRadius
        }
    }
    
    @IBInspectable public var shadowColor: UIColor = UIColor.clear {
        didSet {
            layer.shadowColor = shadowColor.cgColor
        }
    }
    
    @IBInspectable public var shadowRadius: CGFloat = 0 {
        didSet {
            layer.shadowRadius = shadowRadius
        }
    }
    
    @IBInspectable public var shadowOpacity: CGFloat = 0 {
        didSet {
            layer.shadowOpacity = Float(shadowOpacity)
        }
    }
    
    @IBInspectable public var shadowOffsetY: CGFloat = 0 {
        didSet {
            layer.shadowOffset.height = shadowOffsetY
        }
    }

}


================================================
FILE: Spring/DesignableImageView.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

@IBDesignable public class DesignableImageView: SpringImageView {
    
    @IBInspectable public var borderColor: UIColor = UIColor.clear {
        didSet {
            layer.borderColor = borderColor.cgColor
        }
    }
    
    @IBInspectable public var borderWidth: CGFloat = 0 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }
    
    @IBInspectable public var cornerRadius: CGFloat = 0 {
        didSet {
            layer.cornerRadius = cornerRadius
        }
    }

}


================================================
FILE: Spring/DesignableLabel.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

@IBDesignable public class DesignableLabel: SpringLabel {

    @IBInspectable public var lineHeight: CGFloat = 1.5 {
        didSet {
            let font = UIFont(name: self.font.fontName, size: self.font.pointSize)
            guard let text = self.text else { return }
            
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.lineSpacing = lineHeight
            
            let attributedString = NSMutableAttributedString(string: text)
            attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, attributedString.length))
            attributedString.addAttribute(NSAttributedString.Key.font, value: font!, range: NSMakeRange(0, attributedString.length))
            
            self.attributedText = attributedString
        }
    }

}


================================================
FILE: Spring/DesignableTabBarController.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

@IBDesignable class DesignableTabBarController: UITabBarController {
    
    @IBInspectable var normalTint: UIColor = UIColor.clear {
        didSet {
            UITabBar.appearance().tintColor = normalTint
            UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: normalTint], for: UIControl.State())
        }
    }
    
    @IBInspectable var selectedTint: UIColor = UIColor.clear {
        didSet {
            UITabBar.appearance().tintColor = selectedTint
            UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: selectedTint], for:UIControl.State.selected)
        }
    }
    
    @IBInspectable var fontName: String = "" {
        didSet {
            UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: normalTint, NSAttributedString.Key.font: UIFont(name: fontName, size: 11)!], for: UIControl.State())
        }
    }
    
    @IBInspectable var firstSelectedImage: UIImage? {
        didSet {
            if let image = firstSelectedImage {
                var tabBarItems = self.tabBar.items as [UITabBarItem]?
                tabBarItems?[0].selectedImage = image.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
            }
        }
    }
    
    @IBInspectable var secondSelectedImage: UIImage? {
        didSet {
            if let image = secondSelectedImage {
                var tabBarItems = self.tabBar.items as [UITabBarItem]?
                tabBarItems?[1].selectedImage = image.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
            }
        }
    }
    
    @IBInspectable var thirdSelectedImage: UIImage? {
        didSet {
            if let image = thirdSelectedImage {
                var tabBarItems = self.tabBar.items as [UITabBarItem]?
                tabBarItems?[2].selectedImage = image.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
            }
        }
    }
    
    @IBInspectable var fourthSelectedImage: UIImage? {
        didSet {
            if let image = fourthSelectedImage {
                var tabBarItems = self.tabBar.items as [UITabBarItem]?
                tabBarItems?[3].selectedImage = image.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
            }
        }
    }
    
    @IBInspectable var fifthSelectedImage: UIImage? {
        didSet {
            if let image = fifthSelectedImage {
                var tabBarItems = self.tabBar.items as [UITabBarItem]?
                tabBarItems?[4].selectedImage = image.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
            }
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        if let items = self.tabBar.items {
            for item in items {
                if let image = item.image {
                    item.image = image.imageWithColor(tintColor: self.normalTint).withRenderingMode(UIImage.RenderingMode.alwaysOriginal)
                }
            }
        }
    }
}

extension UIImage {
    func imageWithColor(tintColor: UIColor) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
        
        let context = UIGraphicsGetCurrentContext()
        context!.translateBy(x: 0, y: self.size.height)
        context!.scaleBy(x: 1.0, y: -1.0);
        context!.setBlendMode(CGBlendMode.normal)
        
        let rect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)
        context?.clip(to: rect, mask: self.cgImage!)
        tintColor.setFill()
        context!.fill(rect)
        
        let newImage = UIGraphicsGetImageFromCurrentImageContext()! as UIImage
        UIGraphicsEndImageContext()
        
        return newImage
    }
}


================================================
FILE: Spring/DesignableTextField.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

@IBDesignable public class DesignableTextField: SpringTextField {
    
    @IBInspectable public var placeholderColor: UIColor = UIColor.clear {
        didSet {
            guard let placeholder = placeholder else { return }
            attributedPlaceholder = NSAttributedString(string: placeholder, attributes: [NSAttributedString.Key.foregroundColor: placeholderColor])
            layoutSubviews()
            
        }
    }
    
    @IBInspectable public var sidePadding: CGFloat = 0 {
        didSet {
            let padding = UIView(frame: CGRect(x: 0, y: 0, width: sidePadding, height: sidePadding))
            
            leftViewMode = UITextField.ViewMode.always
            leftView = padding
            
            rightViewMode = UITextField.ViewMode.always
            rightView = padding
        }
    }
    
    @IBInspectable public var leftPadding: CGFloat = 0 {
        didSet {
            let padding = UIView(frame: CGRect(x: 0, y: 0, width: leftPadding, height: 0))
            
            leftViewMode = UITextField.ViewMode.always
            leftView = padding
        }
    }
    
    @IBInspectable public var rightPadding: CGFloat = 0 {
        didSet {
            let padding = UIView(frame: CGRect(x: 0, y: 0, width: rightPadding, height: 0))
            
            rightViewMode = UITextField.ViewMode.always
            rightView = padding
        }
    }
    
    @IBInspectable public var borderColor: UIColor = UIColor.clear {
        didSet {
            layer.borderColor = borderColor.cgColor
        }
    }
    
    @IBInspectable public var borderWidth: CGFloat = 0 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }
    
    @IBInspectable public var cornerRadius: CGFloat = 0 {
        didSet {
            layer.cornerRadius = cornerRadius
        }
    }
   
    @IBInspectable public var lineHeight: CGFloat = 1.5 {
        didSet {
            let font = UIFont(name: self.font!.fontName, size: self.font!.pointSize)
            guard let text = self.text else { return }
            
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.lineSpacing = lineHeight
            
            let attributedString = NSMutableAttributedString(string: text)
            attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length))
            attributedString.addAttribute(NSAttributedString.Key.font, value: font!, range: NSRange(location: 0, length: attributedString.length))
            
            self.attributedText = attributedString
        }
    }
    
}


================================================
FILE: Spring/DesignableTextView.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

@IBDesignable public class DesignableTextView: SpringTextView {
    
    @IBInspectable public var borderColor: UIColor = UIColor.clear {
        didSet {
            layer.borderColor = borderColor.cgColor
        }
    }
    
    @IBInspectable public var borderWidth: CGFloat = 0 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }
    
    @IBInspectable public var cornerRadius: CGFloat = 0 {
        didSet {
            layer.cornerRadius = cornerRadius
        }
    }
    
    @IBInspectable public var lineHeight: CGFloat = 1.5 {
        didSet {
            let font = UIFont(name: self.font!.fontName, size: self.font!.pointSize)
            guard let text = self.text else { return }
            
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.lineSpacing = lineHeight
            
            let attributedString = NSMutableAttributedString(string: text)
            attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length))
            attributedString.addAttribute(NSAttributedString.Key.font, value: font!, range: NSRange(location: 0, length: attributedString.length))
            
            self.attributedText = attributedString
        }
    }
    
}


================================================
FILE: Spring/DesignableView.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

@IBDesignable public class DesignableView: SpringView {
    
    @IBInspectable public var borderColor: UIColor = UIColor.clear {
        didSet {
            layer.borderColor = borderColor.cgColor
        }
    }
    
    @IBInspectable public var borderWidth: CGFloat = 0 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }
    
    @IBInspectable public var cornerRadius: CGFloat = 0 {
        didSet {
            layer.cornerRadius = cornerRadius
        }
    }
    
    @IBInspectable public var shadowColor: UIColor = UIColor.clear {
        didSet {
            layer.shadowColor = shadowColor.cgColor
        }
    }
    
    @IBInspectable public var shadowRadius: CGFloat = 0 {
        didSet {
            layer.shadowRadius = shadowRadius
        }
    }
    
    @IBInspectable public var shadowOpacity: CGFloat = 0 {
        didSet {
            layer.shadowOpacity = Float(shadowOpacity)
        }
    }
    
    @IBInspectable public var shadowOffsetY: CGFloat = 0 {
        didSet {
            layer.shadowOffset.height = shadowOffsetY
        }
    }
}


================================================
FILE: Spring/ImageLoader.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2014 Nate Lyman (https://github.com/natelyman/SwiftImageLoader)
//
// 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


public class ImageLoader {
    
    var cache = NSCache<NSString, NSData>()
    
    public class var sharedLoader : ImageLoader {
        struct Static {
            static let instance : ImageLoader = ImageLoader()
        }
        return Static.instance
    }
    
    public func imageForUrl(urlString: String, completionHandler: @escaping(_ image: UIImage?, _ url: String) -> ()) {
        DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async { 
            var data: NSData?
            
            if let dataCache = self.cache.object(forKey: urlString as NSString){
                data = (dataCache) as NSData
                
            }else{
                if (URL(string: urlString) != nil)
                {
                    data = NSData(contentsOf: URL(string: urlString)!)
                    if data != nil {
                        self.cache.setObject(data!, forKey: urlString as NSString)
                    }
                }else{
                    return
                }
            }
            
            if let goodData = data {
                let image = UIImage(data: goodData as Data)
                DispatchQueue.main.async(execute: {() in
                    completionHandler(image, urlString)
                })
                return
            }
            
            let downloadTask: URLSessionDataTask = URLSession.shared.dataTask(with: URL(string: urlString)!, completionHandler: { (data, response, error) -> Void in
                
                if (error != nil) {
                    completionHandler(nil, urlString)
                    return
                }
                
                if data != nil {
                    let image = UIImage(data: data!)
                    self.cache.setObject(data! as NSData, forKey: urlString as NSString)
                    DispatchQueue.main.async(execute: {() in
                        completionHandler(image, urlString)
                    })
                    return
                }
            })
            downloadTask.resume()
            
        }
        
    }
}


================================================
FILE: Spring/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>FMWK</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>$(CURRENT_PROJECT_VERSION)</string>
	<key>NSPrincipalClass</key>
	<string></string>
</dict>
</plist>


================================================
FILE: Spring/KeyboardLayoutConstraint.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 James Tang (j@jamztang.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

#if !os(tvOS)
@available(tvOS, unavailable)
public class KeyboardLayoutConstraint: NSLayoutConstraint {
    
    private var offset : CGFloat = 0
    private var keyboardVisibleHeight : CGFloat = 0
    
    @available(tvOS, unavailable)
    override public func awakeFromNib() {
        super.awakeFromNib()
        
        offset = constant
        
        NotificationCenter.default.addObserver(self, selector: #selector(KeyboardLayoutConstraint.keyboardWillShowNotification(_:)), name: UIWindow.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(KeyboardLayoutConstraint.keyboardWillHideNotification(_:)), name: UIWindow.keyboardWillHideNotification, object: nil)
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    // MARK: Notification
    
    @objc func keyboardWillShowNotification(_ notification: Notification) {
        if let userInfo = notification.userInfo {
            if let frameValue = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
                let frame = frameValue.cgRectValue
                keyboardVisibleHeight = frame.size.height
            }
            
            self.updateConstant()
            switch (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber, userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber) {
            case let (.some(duration), .some(curve)):
                
                let options = UIView.AnimationOptions(rawValue: curve.uintValue)
                
                UIView.animate(
                    withDuration: TimeInterval(duration.doubleValue),
                    delay: 0,
                    options: options,
                    animations: {
                        UIApplication.shared.keyWindow?.layoutIfNeeded()
                        return
                    }, completion: { finished in
                })
            default:
                
                break
            }
            
        }
        
    }
    
    @objc func keyboardWillHideNotification(_ notification: NSNotification) {
        keyboardVisibleHeight = 0
        self.updateConstant()
        
        if let userInfo = notification.userInfo {
            
            switch (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber, userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber) {
            case let (.some(duration), .some(curve)):
                
                let options = UIView.AnimationOptions(rawValue: curve.uintValue)
                
                UIView.animate(
                    withDuration: TimeInterval(duration.doubleValue),
                    delay: 0,
                    options: options,
                    animations: {
                        UIApplication.shared.keyWindow?.layoutIfNeeded()
                        return
                    }, completion: { finished in
                })
            default:
                break
            }
        }
    }
    
    func updateConstant() {
        self.constant = offset + keyboardVisibleHeight
    }
    
}
#endif


================================================
FILE: Spring/LoadingView.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

#if !os(tvOS)
@available(tvOS, unavailable)
public class LoadingView: UIView {

    @IBOutlet public weak var indicatorView: SpringView!

    override public func awakeFromNib() {
        let animation = CABasicAnimation()
        animation.keyPath = "transform.rotation.z"
        animation.fromValue = degreesToRadians(degrees: 0)
        animation.toValue = degreesToRadians(degrees: 360)
        animation.duration = 0.9
        animation.repeatCount = HUGE
        indicatorView.layer.add(animation, forKey: "")
    }

    class func designCodeLoadingView() -> UIView {
        
        return Bundle(for: self).loadNibNamed("LoadingView", owner: self, options: nil)![0] as! UIView
    }
}

public extension UIView {

    struct LoadingViewConstants {
        static let Tag = 1000
    }

    public func showLoading() {

        if self.viewWithTag(LoadingViewConstants.Tag) != nil {
            // If loading view is already found in current view hierachy, do nothing
            return
        }

        let loadingXibView = LoadingView.designCodeLoadingView()
        loadingXibView.frame = self.bounds
        loadingXibView.tag = LoadingViewConstants.Tag
        self.addSubview(loadingXibView)

        loadingXibView.alpha = 0
        SpringAnimation.spring(duration: 0.7, animations: {
            loadingXibView.alpha = 1
        })
    }

    public func hideLoading() {

        if let loadingXibView = self.viewWithTag(LoadingViewConstants.Tag) {
            loadingXibView.alpha = 1

            SpringAnimation.springWithCompletion(duration: 0.7, animations: {
                loadingXibView.alpha = 0
                loadingXibView.transform = CGAffineTransform(scaleX: 3, y: 3)
            }, completion: { (completed) -> Void in
                loadingXibView.removeFromSuperview()
            })
        }
    }

}
#endif


================================================
FILE: Spring/LoadingView.xib
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6254" systemVersion="14B25" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6247"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view contentMode="scaleToFill" id="2ih-bH-VRI" customClass="LoadingView" customModule="Spring" customModuleProvider="target">
            <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            <subviews>
                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="19J-d9-rcw" customClass="SpringView" customModule="Spring" customModuleProvider="target">
                    <rect key="frame" x="275" y="275" width="50" height="50"/>
                    <subviews>
                        <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="loading" translatesAutoresizingMaskIntoConstraints="NO" id="e5h-jP-Opi">
                            <rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
                            <constraints>
                                <constraint firstAttribute="width" constant="50" id="Dtw-Z1-4UD"/>
                                <constraint firstAttribute="height" constant="50" id="rHT-mc-g8o"/>
                            </constraints>
                        </imageView>
                    </subviews>
                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
                    <constraints>
                        <constraint firstAttribute="centerY" secondItem="e5h-jP-Opi" secondAttribute="centerY" id="094-1B-RGw"/>
                        <constraint firstAttribute="height" constant="50" id="1hh-Ra-6oa"/>
                        <constraint firstAttribute="width" constant="50" id="XXZ-SW-6d9"/>
                        <constraint firstAttribute="centerX" secondItem="e5h-jP-Opi" secondAttribute="centerX" id="aTG-c6-H6d"/>
                    </constraints>
                </view>
            </subviews>
            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
            <constraints>
                <constraint firstAttribute="centerY" secondItem="19J-d9-rcw" secondAttribute="centerY" id="DlX-dJ-y4A"/>
                <constraint firstAttribute="centerX" secondItem="19J-d9-rcw" secondAttribute="centerX" id="iTf-dQ-2Ho"/>
            </constraints>
            <connections>
                <outlet property="indicatorView" destination="19J-d9-rcw" id="WZZ-6H-whz"/>
            </connections>
        </view>
    </objects>
    <resources>
        <image name="loading" width="40" height="40"/>
    </resources>
</document>


================================================
FILE: Spring/Misc.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

public extension String {
    var length: Int { return self.count }
    
    func toURL() -> NSURL? {
        return NSURL(string: self)
    }
}

public func htmlToAttributedString(text: String) -> NSAttributedString! {
    guard let htmlData = text.data(using: String.Encoding.utf8, allowLossyConversion: false) else {
        return NSAttributedString() }
    let htmlString: NSAttributedString?
    do {
        htmlString = try NSAttributedString(data: htmlData, options: [NSAttributedString.DocumentReadingOptionKey.documentType:NSAttributedString.DocumentType.html], documentAttributes: nil)
    } catch _ {
        htmlString = nil
    }
    
    return htmlString
}

public func degreesToRadians(degrees: CGFloat) -> CGFloat {
    return degrees * CGFloat(CGFloat.pi / 180)
}

public func delay(delay:Double, closure: @escaping ()->()) {
    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
}

public func imageFromURL(_ Url: String) -> UIImage {
    let url = Foundation.URL(string: Url)
    let data = try? Data(contentsOf: url!)
    return UIImage(data: data!)!
}

public extension UIColor {
    convenience init(hex: String) {
        var red:   CGFloat = 0.0
        var green: CGFloat = 0.0
        var blue:  CGFloat = 0.0
        var alpha: CGFloat = 1.0
        var hex:   String = hex
        
        if hex.hasPrefix("#") {
            let index = hex.index(hex.startIndex, offsetBy: 1)
            hex = String(hex[index...])
        }
        
        let scanner = Scanner(string: hex)
        var hexValue: CUnsignedLongLong = 0
        if scanner.scanHexInt64(&hexValue) {
            switch (hex.count) {
            case 3:
                red   = CGFloat((hexValue & 0xF00) >> 8)       / 15.0
                green = CGFloat((hexValue & 0x0F0) >> 4)       / 15.0
                blue  = CGFloat(hexValue & 0x00F)              / 15.0
            case 4:
                red   = CGFloat((hexValue & 0xF000) >> 12)     / 15.0
                green = CGFloat((hexValue & 0x0F00) >> 8)      / 15.0
                blue  = CGFloat((hexValue & 0x00F0) >> 4)      / 15.0
                alpha = CGFloat(hexValue & 0x000F)             / 15.0
            case 6:
                red   = CGFloat((hexValue & 0xFF0000) >> 16)   / 255.0
                green = CGFloat((hexValue & 0x00FF00) >> 8)    / 255.0
                blue  = CGFloat(hexValue & 0x0000FF)           / 255.0
            case 8:
                red   = CGFloat((hexValue & 0xFF000000) >> 24) / 255.0
                green = CGFloat((hexValue & 0x00FF0000) >> 16) / 255.0
                blue  = CGFloat((hexValue & 0x0000FF00) >> 8)  / 255.0
                alpha = CGFloat(hexValue & 0x000000FF)         / 255.0
            default:
                print("Invalid RGB string, number of characters after '#' should be either 3, 4, 6 or 8", terminator: "")
            }
        } else {
            print("Scan hex error")
        }
        self.init(red:red, green:green, blue:blue, alpha:alpha)
    }
}

public func rgbaToUIColor(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) -> UIColor {
    
    return UIColor(red: red, green: green, blue: blue, alpha: alpha)
}

public func UIColorFromRGB(rgbValue: UInt) -> UIColor {
    return UIColor(
        red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
        green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
        blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
        alpha: CGFloat(1.0)
    )
}

public func stringFromDate(date: NSDate, format: String) -> String {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = format
    return dateFormatter.string(from: date as Date)
}

public func dateFromString(date: String, format: String) -> Date {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = format
    if let date = dateFormatter.date(from: date) {
        return date
    } else {
        return Date(timeIntervalSince1970: 0)
    }
}

public func randomStringWithLength (len : Int) -> NSString {
    
    let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    
    let randomString : NSMutableString = NSMutableString(capacity: len)
    
    for _ in 0 ..< len {
        let length = UInt32 (letters.length)
        let rand = arc4random_uniform(length)
        randomString.appendFormat("%C", letters.character(at: Int(rand)))
    }
    
    return randomString
}

public func timeAgoSinceDate(date: Date, numericDates: Bool) -> String {
    let calendar = Calendar.current
    let unitFlags = Set<Calendar.Component>(arrayLiteral: Calendar.Component.minute, Calendar.Component.hour, Calendar.Component.day, Calendar.Component.weekOfYear, Calendar.Component.month, Calendar.Component.year, Calendar.Component.second)
    let now = Date()
    let dateComparison = now.compare(date)
    var earliest: Date
    var latest: Date
    
    switch dateComparison {
    case .orderedAscending:
        earliest = now
        latest = date
    default:
        earliest = date
        latest = now
    }
    
    let components: DateComponents = calendar.dateComponents(unitFlags, from: earliest, to: latest)
    
    guard
        let year = components.year,
        let month = components.month,
        let weekOfYear = components.weekOfYear,
        let day = components.day,
        let hour = components.hour,
        let minute = components.minute,
        let second = components.second
        else {
        fatalError()
    }
    
    if (year >= 2) {
        return "\(year)y"
    } else if (year >= 1) {
        if (numericDates){
            return "1y"
        } else {
            return "1y"
        }
    } else if (month >= 2) {
        return "\(month * 4)w"
    } else if (month >= 1) {
        if (numericDates){
            return "4w"
        } else {
            return "4w"
        }
    } else if (weekOfYear >= 2) {
        return "\(weekOfYear)w"
    } else if (weekOfYear >= 1){
        if (numericDates){
            return "1w"
        } else {
            return "1w"
        }
    } else if (day >= 2) {
        return "\(components.day ?? 2)d"
    } else if (day >= 1){
        if (numericDates){
            return "1d"
        } else {
            return "1d"
        }
    } else if (hour >= 2) {
        return "\(hour)h"
    } else if (hour >= 1){
        if (numericDates){
            return "1h"
        } else {
            return "1h"
        }
    } else if (minute >= 2) {
        return "\(minute)m"
    } else if (minute >= 1){
        if (numericDates){
            return "1m"
        } else {
            return "1m"
        }
    } else if (second >= 3) {
        return "\(second)s"
    } else {
        return "now"
    }
    
}

extension UIImageView {
    func setImage(url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit, placeholderImage: UIImage?) {
        contentMode = mode
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard
                let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
                let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
                let data = data, error == nil,
                let image = UIImage(data: data)
                else {
                    self.image = placeholderImage
                    return
            }
            DispatchQueue.main.async() { () -> Void in
                self.image = image
                
            }
            }.resume()
    }
    func setImage(urlString: String, contentMode mode: UIView.ContentMode = .scaleAspectFit, placeholderImage: UIImage?) {
        guard let url = URL(string: urlString) else {
            image = placeholderImage
            return
        }
        setImage(url: url, contentMode: mode, placeholderImage: placeholderImage)
    }
}


================================================
FILE: Spring/SoundPlayer.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 James Tang (j@jamztang.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 AudioToolbox

public struct SoundPlayer {
    
    static var filename : String?
    static var enabled : Bool = true
    
    private struct Internal {
        static var cache = [URL:SystemSoundID]()
    }
    
    public static func playSound(soundFile: String) {
        
        if !enabled {
            return
        }
        
        if let url = Bundle.main.url(forResource: soundFile, withExtension: nil) {
            
            var soundID : SystemSoundID = Internal.cache[url] ?? 0
            
            if soundID == 0 {
                AudioServicesCreateSystemSoundID(url as CFURL, &soundID)
                Internal.cache[url] = soundID
            }
            
            AudioServicesPlaySystemSound(soundID)
            
        } else {
            print("Could not find sound file name `\(soundFile)`")
        }
    }
    
    static func play(file: String) {
        self.playSound(soundFile: file)
    }
}


================================================
FILE: Spring/Spring.h
================================================
//
//  Spring.h
//  Spring
//
//  Created by James Tang on 20/1/15.
//  Copyright (c) 2015 Meng To. All rights reserved.
//

#import <UIKit/UIKit.h>

//! Project version number for Spring.
FOUNDATION_EXPORT double SpringVersionNumber;

//! Project version string for Spring.
FOUNDATION_EXPORT const unsigned char SpringVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <Spring/PublicHeader.h>




================================================
FILE: Spring/Spring.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

@objc public protocol Springable {
    var autostart: Bool  { get set }
    var autohide: Bool  { get set }
    var animation: String  { get set }
    var force: CGFloat  { get set }
    var delay: CGFloat { get set }
    var duration: CGFloat { get set }
    var damping: CGFloat { get set }
    var velocity: CGFloat { get set }
    var repeatCount: Float { get set }
    var x: CGFloat { get set }
    var y: CGFloat { get set }
    var scaleX: CGFloat { get set }
    var scaleY: CGFloat { get set }
    var rotate: CGFloat { get set }
    var opacity: CGFloat { get set }
    var animateFrom: Bool { get set }
    var curve: String { get set }
    
    // UIView
    var layer : CALayer { get }
    var transform : CGAffineTransform { get set }
    var alpha : CGFloat { get set }
    
    func animate()
    func animateNext(completion: @escaping () -> ())
    func animateTo()
    func animateToNext(completion: @escaping () -> ())
}

public class Spring : NSObject {
    
    private unowned var view : Springable
    private var shouldAnimateAfterActive = false
    private var shouldAnimateInLayoutSubviews = true
    
    init(_ view: Springable) {
        self.view = view
        super.init()
        commonInit()
    }
    
    func commonInit() {
        NotificationCenter.default.addObserver(self, selector: #selector(Spring.didBecomeActiveNotification(_:)), name: UIApplication.didBecomeActiveNotification, object: nil)
    }
    
    @objc func didBecomeActiveNotification(_ notification: NSNotification) {
        if shouldAnimateAfterActive {
            alpha = 0
            animate()
            shouldAnimateAfterActive = false
        }
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self, name: UIApplication.didBecomeActiveNotification, object: nil)
    }
    
    private var autostart: Bool { set { self.view.autostart = newValue } get { return self.view.autostart }}
    private var autohide: Bool { set { self.view.autohide = newValue } get { return self.view.autohide }}
    private var animation: String { set { self.view.animation = newValue } get { return self.view.animation }}
    private var force: CGFloat { set { self.view.force = newValue } get { return self.view.force }}
    private var delay: CGFloat { set { self.view.delay = newValue } get { return self.view.delay }}
    private var duration: CGFloat { set { self.view.duration = newValue } get { return self.view.duration }}
    private var damping: CGFloat { set { self.view.damping = newValue } get { return self.view.damping }}
    private var velocity: CGFloat { set { self.view.velocity = newValue } get { return self.view.velocity }}
    private var repeatCount: Float { set { self.view.repeatCount = newValue } get { return self.view.repeatCount }}
    private var x: CGFloat { set { self.view.x = newValue } get { return self.view.x }}
    private var y: CGFloat { set { self.view.y = newValue } get { return self.view.y }}
    private var scaleX: CGFloat { set { self.view.scaleX = newValue } get { return self.view.scaleX }}
    private var scaleY: CGFloat { set { self.view.scaleY = newValue } get { return self.view.scaleY }}
    private var rotate: CGFloat { set { self.view.rotate = newValue } get { return self.view.rotate }}
    private var opacity: CGFloat { set { self.view.opacity = newValue } get { return self.view.opacity }}
    private var animateFrom: Bool { set { self.view.animateFrom = newValue } get { return self.view.animateFrom }}
    private var curve: String { set { self.view.curve = newValue } get { return self.view.curve }}
    
    // UIView
    private var layer : CALayer { return view.layer }
    private var transform : CGAffineTransform { get { return view.transform } set { view.transform = newValue }}
    private var alpha: CGFloat { get { return view.alpha } set { view.alpha = newValue } }
    
    public enum AnimationPreset: String {
        case SlideLeft = "slideLeft"
        case SlideRight = "slideRight"
        case SlideDown = "slideDown"
        case SlideUp = "slideUp"
        case SqueezeLeft = "squeezeLeft"
        case SqueezeRight = "squeezeRight"
        case SqueezeDown = "squeezeDown"
        case SqueezeUp = "squeezeUp"
        case FadeIn = "fadeIn"
        case FadeOut = "fadeOut"
        case FadeOutIn = "fadeOutIn"
        case FadeInLeft = "fadeInLeft"
        case FadeInRight = "fadeInRight"
        case FadeInDown = "fadeInDown"
        case FadeInUp = "fadeInUp"
        case ZoomIn = "zoomIn"
        case ZoomOut = "zoomOut"
        case Fall = "fall"
        case Shake = "shake"
        case Pop = "pop"
        case FlipX = "flipX"
        case FlipY = "flipY"
        case Morph = "morph"
        case Squeeze = "squeeze"
        case Flash = "flash"
        case Wobble = "wobble"
        case Swing = "swing"
    }
    
    public enum AnimationCurve: String {
        case EaseIn = "easeIn"
        case EaseOut = "easeOut"
        case EaseInOut = "easeInOut"
        case Linear = "linear"
        case Spring = "spring"
        case EaseInSine = "easeInSine"
        case EaseOutSine = "easeOutSine"
        case EaseInOutSine = "easeInOutSine"
        case EaseInQuad = "easeInQuad"
        case EaseOutQuad = "easeOutQuad"
        case EaseInOutQuad = "easeInOutQuad"
        case EaseInCubic = "easeInCubic"
        case EaseOutCubic = "easeOutCubic"
        case EaseInOutCubic = "easeInOutCubic"
        case EaseInQuart = "easeInQuart"
        case EaseOutQuart = "easeOutQuart"
        case EaseInOutQuart = "easeInOutQuart"
        case EaseInQuint = "easeInQuint"
        case EaseOutQuint = "easeOutQuint"
        case EaseInOutQuint = "easeInOutQuint"
        case EaseInExpo = "easeInExpo"
        case EaseOutExpo = "easeOutExpo"
        case EaseInOutExpo = "easeInOutExpo"
        case EaseInCirc = "easeInCirc"
        case EaseOutCirc = "easeOutCirc"
        case EaseInOutCirc = "easeInOutCirc"
        case EaseInBack = "easeInBack"
        case EaseOutBack = "easeOutBack"
        case EaseInOutBack = "easeInOutBack"
    }
    
    func animatePreset() {
        alpha = 0.99
        if let animation = AnimationPreset(rawValue: animation) {
            switch animation {
            case .SlideLeft:
                x = 300*force
            case .SlideRight:
                x = -300*force
            case .SlideDown:
                y = -300*force
            case .SlideUp:
                y = 300*force
            case .SqueezeLeft:
                x = 300
                scaleX = 3*force
            case .SqueezeRight:
                x = -300
                scaleX = 3*force
            case .SqueezeDown:
                y = -300
                scaleY = 3*force
            case .SqueezeUp:
                y = 300
                scaleY = 3*force
            case .FadeIn:
                opacity = 0
            case .FadeOut:
                animateFrom = false
                opacity = 0
            case .FadeOutIn:
                let animation = CABasicAnimation()
                animation.keyPath = "opacity"
                animation.fromValue = 1
                animation.toValue = 0
                animation.timingFunction = getTimingFunction(curve: curve)
                animation.duration = CFTimeInterval(duration)
                animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay)
                animation.autoreverses = true
                layer.add(animation, forKey: "fade")
            case .FadeInLeft:
                opacity = 0
                x = 300*force
            case .FadeInRight:
                x = -300*force
                opacity = 0
            case .FadeInDown:
                y = -300*force
                opacity = 0
            case .FadeInUp:
                y = 300*force
                opacity = 0
            case .ZoomIn:
                opacity = 0
                scaleX = 2*force
                scaleY = 2*force
            case .ZoomOut:
                animateFrom = false
                opacity = 0
                scaleX = 2*force
                scaleY = 2*force
            case .Fall:
                animateFrom = false
                rotate = 15 * CGFloat(CGFloat.pi/180)
                y = 600*force
            case .Shake:
                let animation = CAKeyframeAnimation()
                animation.keyPath = "position.x"
                animation.values = [0, 30*force, -30*force, 30*force, 0]
                animation.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1]
                animation.timingFunction = getTimingFunction(curve: curve)
                animation.duration = CFTimeInterval(duration)
                animation.isAdditive = true
                animation.repeatCount = repeatCount
                animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay)
                layer.add(animation, forKey: "shake")
            case .Pop:
                let animation = CAKeyframeAnimation()
                animation.keyPath = "transform.scale"
                animation.values = [0, 0.2*force, -0.2*force, 0.2*force, 0]
                animation.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1]
                animation.timingFunction = getTimingFunction(curve: curve)
                animation.duration = CFTimeInterval(duration)
                animation.isAdditive = true
                animation.repeatCount = repeatCount
                animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay)
                layer.add(animation, forKey: "pop")
            case .FlipX:
                rotate = 0
                scaleX = 1
                scaleY = 1
                var perspective = CATransform3DIdentity
                perspective.m34 = -1.0 / layer.frame.size.width/2
                
                let animation = CABasicAnimation()
                animation.keyPath = "transform"
                animation.fromValue = NSValue(caTransform3D: CATransform3DMakeRotation(0, 0, 0, 0))
                animation.toValue = NSValue(caTransform3D:
                    CATransform3DConcat(perspective, CATransform3DMakeRotation(CGFloat(CGFloat.pi), 0, 1, 0)))
                animation.duration = CFTimeInterval(duration)
                animation.repeatCount = repeatCount
                animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay)
                animation.timingFunction = getTimingFunction(curve: curve)
                layer.add(animation, forKey: "3d")
            case .FlipY:
                var perspective = CATransform3DIdentity
                perspective.m34 = -1.0 / layer.frame.size.width/2
                
                let animation = CABasicAnimation()
                animation.keyPath = "transform"
                animation.fromValue = NSValue(caTransform3D:
                    CATransform3DMakeRotation(0, 0, 0, 0))
                animation.toValue = NSValue(caTransform3D:
                    CATransform3DConcat(perspective,CATransform3DMakeRotation(CGFloat(CGFloat.pi), 1, 0, 0)))
                animation.duration = CFTimeInterval(duration)
                animation.repeatCount = repeatCount
                animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay)
                animation.timingFunction = getTimingFunction(curve: curve)
                layer.add(animation, forKey: "3d")
            case .Morph:
                let morphX = CAKeyframeAnimation()
                morphX.keyPath = "transform.scale.x"
                morphX.values = [1, 1.3*force, 0.7, 1.3*force, 1]
                morphX.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1]
                morphX.timingFunction = getTimingFunction(curve: curve)
                morphX.duration = CFTimeInterval(duration)
                morphX.repeatCount = repeatCount
                morphX.beginTime = CACurrentMediaTime() + CFTimeInterval(delay)
                layer.add(morphX, forKey: "morphX")
                
                let morphY = CAKeyframeAnimation()
                morphY.keyPath = "transform.scale.y"
                morphY.values = [1, 0.7, 1.3*force, 0.7, 1]
                morphY.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1]
                morphY.timingFunction = getTimingFunction(curve: curve)
                morphY.duration = CFTimeInterval(duration)
                morphY.repeatCount = repeatCount
                morphY.beginTime = CACurrentMediaTime() + CFTimeInterval(delay)
                layer.add(morphY, forKey: "morphY")
            case .Squeeze:
                let morphX = CAKeyframeAnimation()
                morphX.keyPath = "transform.scale.x"
                morphX.values = [1, 1.5*force, 0.5, 1.5*force, 1]
                morphX.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1]
                morphX.timingFunction = getTimingFunction(curve: curve)
                morphX.duration = CFTimeInterval(duration)
                morphX.repeatCount = repeatCount
                morphX.beginTime = CACurrentMediaTime() + CFTimeInterval(delay)
                layer.add(morphX, forKey: "morphX")
                
                let morphY = CAKeyframeAnimation()
                morphY.keyPath = "transform.scale.y"
                morphY.values = [1, 0.5, 1, 0.5, 1]
                morphY.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1]
                morphY.timingFunction = getTimingFunction(curve: curve)
                morphY.duration = CFTimeInterval(duration)
                morphY.repeatCount = repeatCount
                morphY.beginTime = CACurrentMediaTime() + CFTimeInterval(delay)
                layer.add(morphY, forKey: "morphY")
            case .Flash:
                let animation = CABasicAnimation()
                animation.keyPath = "opacity"
                animation.fromValue = 1
                animation.toValue = 0
                animation.duration = CFTimeInterval(duration)
                animation.repeatCount = repeatCount * 2.0
                animation.autoreverses = true
                animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay)
                layer.add(animation, forKey: "flash")
            case .Wobble:
                let animation = CAKeyframeAnimation()
                animation.keyPath = "transform.rotation"
                animation.values = [0, 0.3*force, -0.3*force, 0.3*force, 0]
                animation.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1]
                animation.duration = CFTimeInterval(duration)
                animation.isAdditive = true
                animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay)
                layer.add(animation, forKey: "wobble")
                
                let x = CAKeyframeAnimation()
                x.keyPath = "position.x"
                x.values = [0, 30*force, -30*force, 30*force, 0]
                x.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1]
                x.timingFunction = getTimingFunction(curve: curve)
                x.duration = CFTimeInterval(duration)
                x.isAdditive = true
                x.repeatCount = repeatCount
                x.beginTime = CACurrentMediaTime() + CFTimeInterval(delay)
                layer.add(x, forKey: "x")
            case .Swing:
                let animation = CAKeyframeAnimation()
                animation.keyPath = "transform.rotation"
                animation.values = [0, 0.3*force, -0.3*force, 0.3*force, 0]
                animation.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1]
                animation.duration = CFTimeInterval(duration)
                animation.isAdditive = true
                animation.repeatCount = repeatCount
                animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay)
                layer.add(animation, forKey: "swing")
            }
        }
    }
    
    func getTimingFunction(curve: String) -> CAMediaTimingFunction {
        if let curve = AnimationCurve(rawValue: curve) {
            switch curve {
            case .EaseIn: return CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
            case .EaseOut: return CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
            case .EaseInOut: return CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
            case .Linear: return CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
            case .Spring: return CAMediaTimingFunction(controlPoints: 0.5, 1.1+Float(force/3), 1, 1)
            case .EaseInSine: return CAMediaTimingFunction(controlPoints: 0.47, 0, 0.745, 0.715)
            case .EaseOutSine: return CAMediaTimingFunction(controlPoints: 0.39, 0.575, 0.565, 1)
            case .EaseInOutSine: return CAMediaTimingFunction(controlPoints: 0.445, 0.05, 0.55, 0.95)
            case .EaseInQuad: return CAMediaTimingFunction(controlPoints: 0.55, 0.085, 0.68, 0.53)
            case .EaseOutQuad: return CAMediaTimingFunction(controlPoints: 0.25, 0.46, 0.45, 0.94)
            case .EaseInOutQuad: return CAMediaTimingFunction(controlPoints: 0.455, 0.03, 0.515, 0.955)
            case .EaseInCubic: return CAMediaTimingFunction(controlPoints: 0.55, 0.055, 0.675, 0.19)
            case .EaseOutCubic: return CAMediaTimingFunction(controlPoints: 0.215, 0.61, 0.355, 1)
            case .EaseInOutCubic: return CAMediaTimingFunction(controlPoints: 0.645, 0.045, 0.355, 1)
            case .EaseInQuart: return CAMediaTimingFunction(controlPoints: 0.895, 0.03, 0.685, 0.22)
            case .EaseOutQuart: return CAMediaTimingFunction(controlPoints: 0.165, 0.84, 0.44, 1)
            case .EaseInOutQuart: return CAMediaTimingFunction(controlPoints: 0.77, 0, 0.175, 1)
            case .EaseInQuint: return CAMediaTimingFunction(controlPoints: 0.755, 0.05, 0.855, 0.06)
            case .EaseOutQuint: return CAMediaTimingFunction(controlPoints: 0.23, 1, 0.32, 1)
            case .EaseInOutQuint: return CAMediaTimingFunction(controlPoints: 0.86, 0, 0.07, 1)
            case .EaseInExpo: return CAMediaTimingFunction(controlPoints: 0.95, 0.05, 0.795, 0.035)
            case .EaseOutExpo: return CAMediaTimingFunction(controlPoints: 0.19, 1, 0.22, 1)
            case .EaseInOutExpo: return CAMediaTimingFunction(controlPoints: 1, 0, 0, 1)
            case .EaseInCirc: return CAMediaTimingFunction(controlPoints: 0.6, 0.04, 0.98, 0.335)
            case .EaseOutCirc: return CAMediaTimingFunction(controlPoints: 0.075, 0.82, 0.165, 1)
            case .EaseInOutCirc: return CAMediaTimingFunction(controlPoints: 0.785, 0.135, 0.15, 0.86)
            case .EaseInBack: return CAMediaTimingFunction(controlPoints: 0.6, -0.28, 0.735, 0.045)
            case .EaseOutBack: return CAMediaTimingFunction(controlPoints: 0.175, 0.885, 0.32, 1.275)
            case .EaseInOutBack: return CAMediaTimingFunction(controlPoints: 0.68, -0.55, 0.265, 1.55)
            }
        }
        return CAMediaTimingFunction(name: CAMediaTimingFunctionName.default)
    }
    
    func getAnimationOptions(curve: String) -> UIView.AnimationOptions {
        if let curve = AnimationCurve(rawValue: curve) {
            switch curve {
            case .EaseIn: return UIView.AnimationOptions.curveEaseIn
            case .EaseOut: return UIView.AnimationOptions.curveEaseOut
            case .EaseInOut: return UIView.AnimationOptions()
            default: break
            }
        }
        return UIView.AnimationOptions.curveLinear
    }
    
    public func animate() {
        animateFrom = true
        animatePreset()
        setView {}
    }
    
    public func animateNext(completion: @escaping () -> ()) {
        animateFrom = true
        animatePreset()
        setView {
            completion()
        }
    }
    
    public func animateTo() {
        animateFrom = false
        animatePreset()
        setView {}
    }
    
    public func animateToNext(completion: @escaping () -> ()) {
        animateFrom = false
        animatePreset()
        setView {
            completion()
        }
    }
    
    public func customAwakeFromNib() {
        if autohide {
            alpha = 0
        }
    }
    
    public func customLayoutSubviews() {
        if shouldAnimateInLayoutSubviews {
            shouldAnimateInLayoutSubviews = false
            if autostart {
                if UIApplication.shared.applicationState != .active {
                    shouldAnimateAfterActive = true
                    return
                }
                alpha = 0
                animate()
            }
        }
    }
    
    func setView(completion: @escaping () -> ()) {
        if animateFrom {
            let translate = CGAffineTransform(translationX: self.x, y: self.y)
            let scale = CGAffineTransform(scaleX: self.scaleX, y: self.scaleY)
            let rotate = CGAffineTransform(rotationAngle: self.rotate)
            let translateAndScale = translate.concatenating(scale)
            self.transform = rotate.concatenating(translateAndScale)
            
            self.alpha = self.opacity
        }
        
        UIView.animate( withDuration: TimeInterval(duration),
                        delay: TimeInterval(delay),
                        usingSpringWithDamping: damping,
                        initialSpringVelocity: velocity,
                        options: [getAnimationOptions(curve: curve), UIView.AnimationOptions.allowUserInteraction],
                        animations: { [weak self] in
                            if let _self = self
                            {
                                if _self.animateFrom {
                                    _self.transform = CGAffineTransform.identity
                                    _self.alpha = 1
                                }
                                else {
                                    let translate = CGAffineTransform(translationX: _self.x, y: _self.y)
                                    let scale = CGAffineTransform(scaleX: _self.scaleX, y: _self.scaleY)
                                    let rotate = CGAffineTransform(rotationAngle: _self.rotate)
                                    let translateAndScale = translate.concatenating(scale)
                                    _self.transform = rotate.concatenating(translateAndScale)
                                    
                                    _self.alpha = _self.opacity
                                }
                                
                            }
                            
            }, completion: { [weak self] finished in
                
                completion()
                self?.resetAll()
                
        })
        
    }
    
    func reset() {
        x = 0
        y = 0
        opacity = 1
    }
    
    func resetAll() {
        x = 0
        y = 0
        animation = ""
        opacity = 1
        scaleX = 1
        scaleY = 1
        rotate = 0
        damping = 0.7
        velocity = 0.7
        repeatCount = 1
        delay = 0
        duration = 0.7
    }
    
}


================================================
FILE: Spring/SpringAnimation.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

@objc public class SpringAnimation: NSObject {
    public class func spring(duration: TimeInterval, animations: @escaping () -> Void) {
        UIView.animate(
            withDuration: duration,
            delay: 0,
            usingSpringWithDamping: 0.7,
            initialSpringVelocity: 0.7,
            options: [],
            animations: {
                animations()
            },
            completion: nil
        )
    }

    public class func springEaseIn(duration: TimeInterval, animations: (() -> Void)!) {
        UIView.animate(
            withDuration: duration,
            delay: 0,
            options: .curveEaseIn,
            animations: {
                animations()
            },
            completion: nil
        )
    }

    public class func springEaseOut(duration: TimeInterval, animations: (() -> Void)!) {
        UIView.animate(
            withDuration: duration,
            delay: 0,
            options: .curveEaseOut,
            animations: {
                animations()
            }, completion: nil
        )
    }

    public class func springEaseInOut(duration: TimeInterval, animations: (() -> Void)!) {
        UIView.animate(
            withDuration: duration,
            delay: 0,
            options: UIView.AnimationOptions(),
            animations: {
                animations()
            }, completion: nil
        )
    }

    public class func springLinear(duration: TimeInterval, animations: (() -> Void)!) {
        UIView.animate(
            withDuration: duration,
            delay: 0,
            options: .curveLinear,
            animations: {
                animations()
            }, completion: nil
        )
    }

    public class func springWithDelay(duration: TimeInterval, delay: TimeInterval, animations: (() -> Void)!) {
        UIView.animate(
            withDuration: duration,
            delay: delay,
            usingSpringWithDamping: 0.7,
            initialSpringVelocity: 0.7,
            options: [],
            animations: {
                animations()
            }, completion: nil
        )
    }

    public class func springWithCompletion(duration: TimeInterval, animations: (() -> Void)!, completion: ((Bool) -> Void)!) {
        UIView.animate(
            withDuration: duration,
            delay: 0,
            usingSpringWithDamping: 0.7,
            initialSpringVelocity: 0.7,
            options: [],
            animations: {
                animations()
            }, completion: { finished in
                completion(finished)
            }
        )
    }
}


================================================
FILE: Spring/SpringButton.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

open class SpringButton: UIButton, Springable {
    @IBInspectable public var autostart: Bool = false
    @IBInspectable public var autohide: Bool = false
    @IBInspectable public var animation: String = ""
    @IBInspectable public var force: CGFloat = 1
    @IBInspectable public var delay: CGFloat = 0
    @IBInspectable public var duration: CGFloat = 0.7
    @IBInspectable public var damping: CGFloat = 0.7
    @IBInspectable public var velocity: CGFloat = 0.7
    @IBInspectable public var repeatCount: Float = 1
    @IBInspectable public var x: CGFloat = 0
    @IBInspectable public var y: CGFloat = 0
    @IBInspectable public var scaleX: CGFloat = 1
    @IBInspectable public var scaleY: CGFloat = 1
    @IBInspectable public var rotate: CGFloat = 0
    @IBInspectable public var curve: String = ""
    public var opacity: CGFloat = 1
    public var animateFrom: Bool = false

    lazy private var spring : Spring = Spring(self)

    override open func awakeFromNib() {
        super.awakeFromNib()
        self.spring.customAwakeFromNib()
    }

    open override func layoutSubviews() {
        super.layoutSubviews()
        spring.customLayoutSubviews()
    }

    public func animate() {
        self.spring.animate()
    }

    public func animateNext(completion: @escaping () -> ()) {
        self.spring.animateNext(completion: completion)
    }

    public func animateTo() {
        self.spring.animateTo()
    }

    public func animateToNext(completion: @escaping () -> ()) {
        self.spring.animateToNext(completion: completion)
    }
}


================================================
FILE: Spring/SpringImageView.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

open class SpringImageView: UIImageView, Springable {
    @IBInspectable public var autostart: Bool = false
    @IBInspectable public var autohide: Bool = false
    @IBInspectable public var animation: String = ""
    @IBInspectable public var force: CGFloat = 1
    @IBInspectable public var delay: CGFloat = 0
    @IBInspectable public var duration: CGFloat = 0.7
    @IBInspectable public var damping: CGFloat = 0.7
    @IBInspectable public var velocity: CGFloat = 0.7
    @IBInspectable public var repeatCount: Float = 1
    @IBInspectable public var x: CGFloat = 0
    @IBInspectable public var y: CGFloat = 0
    @IBInspectable public var scaleX: CGFloat = 1
    @IBInspectable public var scaleY: CGFloat = 1
    @IBInspectable public var rotate: CGFloat = 0
    @IBInspectable public var curve: String = ""
    public var opacity: CGFloat = 1
    public var animateFrom: Bool = false

    lazy private var spring : Spring = Spring(self)

    override open func awakeFromNib() {
        super.awakeFromNib()
        self.spring.customAwakeFromNib()
    }

    open override func layoutSubviews() {
        super.layoutSubviews()
        spring.customLayoutSubviews()
    }

    public func animate() {
        self.spring.animate()
    }

    public func animateNext(completion: @escaping () -> ()) {
        self.spring.animateNext(completion: completion)
    }

    public func animateTo() {
        self.spring.animateTo()
    }

    public func animateToNext(completion: @escaping () -> ()) {
        self.spring.animateToNext(completion: completion)
    }

}


================================================
FILE: Spring/SpringLabel.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

open class SpringLabel: UILabel, Springable {
    @IBInspectable public var autostart: Bool = false
    @IBInspectable public var autohide: Bool = false
    @IBInspectable public var animation: String = ""
    @IBInspectable public var force: CGFloat = 1
    @IBInspectable public var delay: CGFloat = 0
    @IBInspectable public var duration: CGFloat = 0.7
    @IBInspectable public var damping: CGFloat = 0.7
    @IBInspectable public var velocity: CGFloat = 0.7
    @IBInspectable public var repeatCount: Float = 1
    @IBInspectable public var x: CGFloat = 0
    @IBInspectable public var y: CGFloat = 0
    @IBInspectable public var scaleX: CGFloat = 1
    @IBInspectable public var scaleY: CGFloat = 1
    @IBInspectable public var rotate: CGFloat = 0
    @IBInspectable public var curve: String = ""
    public var opacity: CGFloat = 1
    public var animateFrom: Bool = false

    lazy private var spring : Spring = Spring(self)

    override open func awakeFromNib() {
        super.awakeFromNib()
        self.spring.customAwakeFromNib()
    }

    open override func layoutSubviews() {
        super.layoutSubviews()
        spring.customLayoutSubviews()
    }

    public func animate() {
        self.spring.animate()
    }

    public func animateNext(completion: @escaping () -> ()) {
        self.spring.animateNext(completion: completion)
    }

    public func animateTo() {
        self.spring.animateTo()
    }

    public func animateToNext(completion: @escaping () -> ()) {
        self.spring.animateToNext(completion: completion)
    }

}


================================================
FILE: Spring/SpringTextField.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

open class SpringTextField: UITextField, Springable {
    @IBInspectable public var autostart: Bool = false
    @IBInspectable public var autohide: Bool = false
    @IBInspectable public var animation: String = ""
    @IBInspectable public var force: CGFloat = 1
    @IBInspectable public var delay: CGFloat = 0
    @IBInspectable public var duration: CGFloat = 0.7
    @IBInspectable public var damping: CGFloat = 0.7
    @IBInspectable public var velocity: CGFloat = 0.7
    @IBInspectable public var repeatCount: Float = 1
    @IBInspectable public var x: CGFloat = 0
    @IBInspectable public var y: CGFloat = 0
    @IBInspectable public var scaleX: CGFloat = 1
    @IBInspectable public var scaleY: CGFloat = 1
    @IBInspectable public var rotate: CGFloat = 0
    @IBInspectable public var curve: String = ""
    public var opacity: CGFloat = 1
    public var animateFrom: Bool = false

    lazy private var spring : Spring = Spring(self)

    override open func awakeFromNib() {
        super.awakeFromNib()
        self.spring.customAwakeFromNib()
    }

    open override func layoutSubviews() {
        super.layoutSubviews()
        spring.customLayoutSubviews()
    }

    public func animate() {
        self.spring.animate()
    }

    public func animateNext(completion: @escaping () -> ()) {
        self.spring.animateNext(completion: completion)
    }

    public func animateTo() {
        self.spring.animateTo()
    }

    public func animateToNext(completion: @escaping () -> ()) {
        self.spring.animateToNext(completion: completion)
    }
}


================================================
FILE: Spring/SpringTextView.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

open class SpringTextView: UITextView, Springable {
    @IBInspectable public var autostart: Bool = false
    @IBInspectable public var autohide: Bool = false
    @IBInspectable public var animation: String = ""
    @IBInspectable public var force: CGFloat = 1
    @IBInspectable public var delay: CGFloat = 0
    @IBInspectable public var duration: CGFloat = 0.7
    @IBInspectable public var damping: CGFloat = 0.7
    @IBInspectable public var velocity: CGFloat = 0.7
    @IBInspectable public var repeatCount: Float = 1
    @IBInspectable public var x: CGFloat = 0
    @IBInspectable public var y: CGFloat = 0
    @IBInspectable public var scaleX: CGFloat = 1
    @IBInspectable public var scaleY: CGFloat = 1
    @IBInspectable public var rotate: CGFloat = 0
    @IBInspectable public var curve: String = ""
    public var opacity: CGFloat = 1
    public var animateFrom: Bool = false

    lazy private var spring : Spring = Spring(self)

    override open func awakeFromNib() {
        super.awakeFromNib()
        self.spring.customAwakeFromNib()
    }

    open override func layoutSubviews() {
        super.layoutSubviews()
        spring.customLayoutSubviews()
    }

    public func animate() {
        self.spring.animate()
    }

    public func animateNext(completion: @escaping () -> ()) {
        self.spring.animateNext(completion: completion)
    }

    public func animateTo() {
        self.spring.animateTo()
    }

    public func animateToNext(completion: @escaping () -> ()) {
        self.spring.animateToNext(completion: completion)
    }

}


================================================
FILE: Spring/SpringView.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

open class SpringView: UIView, Springable {
    @IBInspectable public var autostart: Bool = false
    @IBInspectable public var autohide: Bool = false
    @IBInspectable public var animation: String = ""
    @IBInspectable public var force: CGFloat = 1
    @IBInspectable public var delay: CGFloat = 0
    @IBInspectable public var duration: CGFloat = 0.7
    @IBInspectable public var damping: CGFloat = 0.7
    @IBInspectable public var velocity: CGFloat = 0.7
    @IBInspectable public var repeatCount: Float = 1
    @IBInspectable public var x: CGFloat = 0
    @IBInspectable public var y: CGFloat = 0
    @IBInspectable public var scaleX: CGFloat = 1
    @IBInspectable public var scaleY: CGFloat = 1
    @IBInspectable public var rotate: CGFloat = 0
    @IBInspectable public var curve: String = ""
    public var opacity: CGFloat = 1
    public var animateFrom: Bool = false

    lazy private var spring : Spring = Spring(self)

    override open func awakeFromNib() {
        super.awakeFromNib()
        self.spring.customAwakeFromNib()
    }

    open override func layoutSubviews() {
        super.layoutSubviews()
        spring.customLayoutSubviews()
    }
    
    public func animate() {
        self.spring.animate()
    }

    public func animateNext(completion: @escaping () -> ()) {
        self.spring.animateNext(completion: completion)
    }

    public func animateTo() {
        self.spring.animateTo()
    }

    public func animateToNext(completion: @escaping () -> ()) {
        self.spring.animateToNext(completion: completion)
    }
}


================================================
FILE: Spring/TransitionManager.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

public class TransitionManager: NSObject, UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning {
    
    var isPresenting = true
    var duration = 0.3
    
    public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let container = transitionContext.containerView
        let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)!
        let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)!
        
        if isPresenting {
            toView.frame = container.bounds
            toView.transform = CGAffineTransform(translationX: 0, y: container.frame.size.height)
            container.addSubview(fromView)
            container.addSubview(toView)
            SpringAnimation.springEaseInOut(duration: duration) {
                fromView.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
                fromView.alpha = 0.5
                toView.transform = CGAffineTransform.identity
            }
        }
        else {

            // 1. Rotating will change the bounds
            // 2. we have to properly reset toView
            // to the actual container's bounds, at
            // the same time take consideration of
            // previous transformation when presenting
            let transform = toView.transform
            toView.transform = CGAffineTransform.identity
            toView.frame = container.bounds
            toView.transform = transform

            container.addSubview(toView)
            container.addSubview(fromView)

            SpringAnimation.springEaseInOut(duration: duration) {
                fromView.transform = CGAffineTransform(translationX: 0, y: fromView.frame.size.height)
                toView.transform = CGAffineTransform.identity
                toView.alpha = 1
            }
        }
        
        delay(delay: duration, closure: {
            transitionContext.completeTransition(true)
        })
    }
    
    public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return duration
    }
    
    public func animationController(forPresentedController presented: UIViewController, presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        isPresenting = true
        return self
    }
    
    public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        isPresenting = false
        return self
    }
}


================================================
FILE: Spring/TransitionZoom.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

public class TransitionZoom: NSObject, UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning {
    
    var isPresenting = true
    var duration = 0.4
    
    public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let container = transitionContext.containerView
        let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)!
        let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)!
        
        if isPresenting {
            container.addSubview(fromView)
            container.addSubview(toView)
            
            toView.alpha = 0
            toView.transform = CGAffineTransform(scaleX: 2, y: 2)

            SpringAnimation.springEaseInOut(duration: duration) {
                fromView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
                fromView.alpha = 0
                toView.transform = CGAffineTransform.identity
                toView.alpha = 1
            }
        }
        else {
            container.addSubview(toView)
            container.addSubview(fromView)
            
            SpringAnimation.springEaseInOut(duration: duration) {
                fromView.transform = CGAffineTransform(scaleX: 2, y: 2)
                fromView.alpha = 0
                toView.transform = CGAffineTransform(scaleX: 1, y: 1)
                toView.alpha = 1
            }
        }
        
        delay(delay: duration, closure: {
            transitionContext.completeTransition(true)
        })
    }
    
    public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return duration
    }
    
    public func animationController(forPresentedController presented: UIViewController, presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        isPresenting = true
        return self
    }
    
    public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        isPresenting = false
        return self
    }
}


================================================
FILE: Spring/UnwindSegue.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2015 Meng To (meng@designcode.io)
//
// 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

public extension UIViewController {
    @IBAction public func unwindToViewController (_ segue: UIStoryboardSegue){}
}


================================================
FILE: Spring.podspec
================================================
Pod::Spec.new do |s|
  s.name = 'Spring'
  s.version = '1.0.6'
  s.license = 'MIT'
  s.summary = 'A library to simplify iOS animations in Swift.'
  s.homepage = 'https://github.com/MengTo/Spring'
  s.authors = { 'Meng To' => 'meng@designcode.io' }
  s.source = { :git => 'https://github.com/MengTo/Spring.git', :tag => s.version.to_s }
  s.requires_arc = true
  s.ios.deployment_target = '8.0'
  s.tvos.deployment_target = '11.0'
  s.source_files = 'Spring/*.swift'
  s.ios.resources = ['Spring/*.xib', 'SpringApp/*.xcassets']
  s.tvos.resources = ['SpringApp/*.xcassets']
end


================================================
FILE: SpringApp/AppDelegate.swift
================================================
//
//  AppDelegate.swift
//  SpringApp
//
//  Created by Meng To on 2015-01-06.
//  Copyright (c) 2015 Meng To. All rights reserved.
//

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func applicationDidFinishLaunching(_ application: UIApplication) {
        // Override point for customization after application launch.
    }

    func applicationWillResignActive(_ application: UIApplication) {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    }

    func applicationDidEnterBackground(_ application: UIApplication) {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    }

    func applicationWillEnterForeground(_ application: UIApplication) {
        // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
    }

    func applicationDidBecomeActive(_ application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }

    func applicationWillTerminate(_ application: UIApplication) {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    }


}



================================================
FILE: SpringApp/Base.lproj/LaunchScreen.xib
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11173.2" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES">
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11173.2"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view contentMode="scaleToFill" id="UpM-yt-Mha">
            <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            <subviews>
                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="HyS-sC-NOj">
                    <color key="backgroundColor" red="0.92941176469999998" green="0.94509803920000002" blue="0.96078431369999995" alpha="0.5" colorSpace="custom" customColorSpace="sRGB"/>
                    <constraints>
                        <constraint firstAttribute="height" constant="260" id="ZzL-k1-Chw"/>
                    </constraints>
                </view>
                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="t7P-Rw-nWV">
                    <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                </view>
            </subviews>
            <color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
            <constraints>
                <constraint firstItem="t7P-Rw-nWV" firstAttribute="leading" secondItem="UpM-yt-Mha" secondAttribute="leading" id="7kg-00-bKX"/>
                <constraint firstAttribute="trailing" secondItem="t7P-Rw-nWV" secondAttribute="trailing" id="D2z-sY-gsJ"/>
                <constraint firstItem="HyS-sC-NOj" firstAttribute="top" secondItem="t7P-Rw-nWV" secondAttribute="bottom" id="M41-Bz-daQ"/>
                <constraint firstItem="HyS-sC-NOj" firstAttribute="leading" secondItem="UpM-yt-Mha" secondAttribute="leading" id="OyT-BU-HGL"/>
                <constraint firstItem="t7P-Rw-nWV" firstAttribute="top" secondItem="UpM-yt-Mha" secondAttribute="top" id="RbV-ub-MW2"/>
                <constraint firstAttribute="trailing" secondItem="HyS-sC-NOj" secondAttribute="trailing" id="dPv-Rb-3j2"/>
                <constraint firstAttribute="bottom" secondItem="HyS-sC-NOj" secondAttribute="bottom" id="un2-ep-uEa"/>
            </constraints>
            <nil key="simulatedStatusBarMetrics"/>
            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
            <point key="canvasLocation" x="548" y="455"/>
        </view>
    </objects>
</document>


================================================
FILE: SpringApp/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11201" systemVersion="16A323" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="uF9-EU-g6i">
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11161"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <customFonts key="customFonts">
        <array key="AvenirNext.ttc">
            <string>AvenirNext-DemiBold</string>
            <string>AvenirNext-Regular</string>
        </array>
    </customFonts>
    <scenes>
        <!--Spring View Controller-->
        <scene sceneID="BLg-45-6qz">
            <objects>
                <viewController id="uF9-EU-g6i" customClass="SpringViewController" customModule="SpringApp" customModuleProvider="target" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="sBl-ke-ld6"/>
                        <viewControllerLayoutGuide type="bottom" id="9I4-Bi-XrU"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="PVY-HA-UuV">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jEx-iK-Wpm">
                                <subviews>
                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="pm9-y7-HlR" customClass="SpringView" customModule="Spring">
                                        <color key="backgroundColor" red="0.1529411765" green="0.61176470589999998" blue="0.92156862750000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <constraints>
                                            <constraint firstAttribute="width" constant="100" id="gmq-7q-DaV"/>
                                            <constraint firstAttribute="height" constant="100" id="v07-I1-obA"/>
                                        </constraints>
                                        <userDefinedRuntimeAttributes>
                                            <userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
                                                <integer key="value" value="10"/>
                                            </userDefinedRuntimeAttribute>
                                            <userDefinedRuntimeAttribute type="string" keyPath="animation" value="shake"/>
                                            <userDefinedRuntimeAttribute type="boolean" keyPath="animateFrom" value="YES"/>
                                        </userDefinedRuntimeAttributes>
                                    </view>
                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kW9-eS-V9V">
                                        <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
                                        <state key="normal">
                                            <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        </state>
                                        <connections>
                                            <action selector="ballButtonPressed:" destination="uF9-EU-g6i" eventType="touchUpInside" id="dwB-lg-MWn"/>
                                        </connections>
                                    </button>
                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hQw-No-MzG">
                                        <constraints>
                                            <constraint firstAttribute="width" constant="100" id="bV4-jl-2ic"/>
                                            <constraint firstAttribute="height" constant="44" id="z4W-XA-EwU"/>
                                        </constraints>
                                        <fontDescription key="fontDescription" name="AvenirNext-Regular" family="Avenir Next" pointSize="20"/>
                                        <color key="tintColor" red="0.1529411765" green="0.61176470589999998" blue="0.92156862750000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <state key="normal" title="Options">
                                            <color key="titleShadowColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
                                        </state>
                                        <connections>
                                            <segue destination="TLI-qL-rpM" kind="presentation" animates="NO" modalPresentationStyle="overCurrentContext" id="gkI-YH-5O4"/>
                                        </connections>
                                    </button>
                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="XZL-Vb-0wq">
                                        <constraints>
                                            <constraint firstAttribute="height" constant="44" id="76m-Hg-JIu"/>
                                            <constraint firstAttribute="height" constant="44" id="8jj-4J-qlA"/>
                                            <constraint firstAttribute="width" constant="100" id="SeB-ve-Lhd"/>
                                            <constraint firstAttribute="width" constant="100" id="ui4-JW-eAd"/>
                                        </constraints>
                                        <fontDescription key="fontDescription" name="AvenirNext-Regular" family="Avenir Next" pointSize="20"/>
                                        <color key="tintColor" red="0.1529411765" green="0.61176470589999998" blue="0.92156862750000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <state key="normal" title="Code">
                                            <color key="titleShadowColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
                                        </state>
                                        <variation key="default">
                                            <mask key="constraints">
                                                <exclude reference="76m-Hg-JIu"/>
                                                <exclude reference="ui4-JW-eAd"/>
                                            </mask>
                                        </variation>
                                        <connections>
                                            <segue destination="gI9-MU-NP6" kind="presentation" animates="NO" modalPresentationStyle="overCurrentContext" id="FuN-YA-SEn"/>
                                        </connections>
                                    </button>
                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Nak-D3-XzB">
                                        <constraints>
                                            <constraint firstAttribute="height" constant="44" id="6uK-hP-N9l"/>
                                            <constraint firstAttribute="width" constant="100" id="yhA-dX-cdB"/>
                                        </constraints>
                                        <color key="tintColor" red="0.1529411765" green="0.61176470589999998" blue="0.92156862750000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <state key="normal" image="icon-shape"/>
                                        <connections>
                                            <action selector="shapeButtonPressed:" destination="uF9-EU-g6i" eventType="touchUpInside" id="H2Y-y2-Jgx"/>
                                        </connections>
                                    </button>
                                </subviews>
                                <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
                                <constraints>
                                    <constraint firstAttribute="bottom" secondItem="kW9-eS-V9V" secondAttribute="bottom" id="6uT-yo-OAF"/>
                                    <constraint firstAttribute="centerX" secondItem="pm9-y7-HlR" secondAttribute="centerX" id="7hO-9B-uxH"/>
                                    <constraint firstAttribute="trailing" secondItem="kW9-eS-V9V" secondAttribute="trailing" id="DER-GK-DCU"/>
                                    <constraint firstAttribute="centerY" secondItem="pm9-y7-HlR" secondAttribute="centerY" id="EuU-FB-fE7"/>
                                    <constraint firstItem="hQw-No-MzG" firstAttribute="leading" secondItem="Nak-D3-XzB" secondAttribute="trailing" id="GK9-2Y-Mc1"/>
                                    <constraint firstItem="kW9-eS-V9V" firstAttribute="leading" secondItem="jEx-iK-Wpm" secondAttribute="leading" id="Go6-NB-BBU"/>
                                    <constraint firstAttribute="trailing" secondItem="hQw-No-MzG" secondAttribute="trailing" id="Gya-jY-yaA"/>
                                    <constraint firstItem="XZL-Vb-0wq" firstAttribute="top" secondItem="jEx-iK-Wpm" secondAttribute="top" constant="20" id="Nuc-Gf-M1Y"/>
                                    <constraint firstItem="kW9-eS-V9V" firstAttribute="top" secondItem="jEx-iK-Wpm" secondAttribute="top" id="UFX-jh-H2F"/>
                                    <constraint firstAttribute="bottom" secondItem="hQw-No-MzG" secondAttribute="bottom" constant="10" id="bR8-aE-oYG"/>
                                    <constraint firstAttribute="trailing" secondItem="XZL-Vb-0wq" secondAttribute="trailing" constant="15" id="h32-ZG-FIX"/>
                                    <constraint firstAttribute="bottom" secondItem="XZL-Vb-0wq" secondAttribute="bottom" constant="10" id="imJ-3U-hM9"/>
                                    <constraint firstItem="XZL-Vb-0wq" firstAttribute="leading" secondItem="jEx-iK-Wpm" secondAttribute="leading" id="nLI-BM-55T"/>
                                    <constraint firstAttribute="bottom" secondItem="Nak-D3-XzB" secondAttribute="bottom" constant="10" id="p1y-0S-Mf1"/>
                                    <constraint firstAttribute="centerX" secondItem="Nak-D3-XzB" secondAttribute="centerX" id="rRq-q4-xqB">
                                        <variation key="heightClass=compact-widthClass=regular" constant="-250"/>
                                    </constraint>
                                </constraints>
                                <variation key="default">
                                    <mask key="constraints">
                                        <exclude reference="Nuc-Gf-M1Y"/>
                                        <exclude reference="h32-ZG-FIX"/>
                                        <exclude reference="GK9-2Y-Mc1"/>
                                    </mask>
                                </variation>
                                <variation key="heightClass=compact-widthClass=regular">
                                    <mask key="constraints">
                                        <exclude reference="rRq-q4-xqB"/>
                                        <include reference="GK9-2Y-Mc1"/>
                                    </mask>
                                </variation>
                            </view>
                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Mpa-80-hgk">
                                <subviews>
                                    <pickerView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="afX-4m-2dp"/>
                                    <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="1" maxValue="5" translatesAutoresizingMaskIntoConstraints="NO" id="Vkq-iS-03i">
                                        <constraints>
                                            <constraint firstAttribute="width" constant="86" id="f1W-Wg-bij"/>
                                            <constraint firstAttribute="height" constant="30" id="kQO-qr-wGX"/>
                                        </constraints>
                                        <color key="minimumTrackTintColor" red="0.1529411765" green="0.61176470589999998" blue="0.92156862750000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <color key="maximumTrackTintColor" red="0.51576060056686401" green="0.54782974720001221" blue="0.62907493114471436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <connections>
                                            <action selector="forceSliderChanged:" destination="uF9-EU-g6i" eventType="valueChanged" id="sex-e8-E9L"/>
                                        </connections>
                                    </slider>
                                    <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.5" maxValue="5" translatesAutoresizingMaskIntoConstraints="NO" id="AkI-y0-NKD">
                                        <constraints>
                                            <constraint firstAttribute="width" constant="86" id="7nQ-OH-2r6"/>
                                        </constraints>
                                        <color key="minimumTrackTintColor" red="0.1529411765" green="0.61176470589999998" blue="0.92156862750000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <color key="maximumTrackTintColor" red="0.51576060056686401" green="0.54782974720001221" blue="0.62907493114471436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <connections>
                                            <action selector="durationSliderChanged:" destination="uF9-EU-g6i" eventType="valueChanged" id="MIQ-6p-sqg"/>
                                        </connections>
                                    </slider>
                                    <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="5" translatesAutoresizingMaskIntoConstraints="NO" id="8lZ-4r-F62">
                                        <color key="tintColor" red="0.1529411765" green="0.61176470589999998" blue="0.92156862750000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <constraints>
                                            <constraint firstAttribute="width" constant="86" id="JnO-NC-shv"/>
                                            <constraint firstAttribute="height" constant="30" id="PyR-VU-Abr"/>
                                        </constraints>
                                        <color key="minimumTrackTintColor" red="0.1529411765" green="0.61176470589999998" blue="0.92156862750000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <color key="maximumTrackTintColor" red="0.51576060056686401" green="0.54782974720001221" blue="0.62907493114471436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <connections>
                                            <action selector="delaySliderChanged:" destination="uF9-EU-g6i" eventType="valueChanged" id="pAg-L8-Z7q"/>
                                        </connections>
                                    </slider>
                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Force: 1" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hZO-3H-UCU">
                                        <fontDescription key="fontDescription" name="Avenir-Heavy" family="Avenir" pointSize="12"/>
                                        <color key="textColor" red="0.51576060056686401" green="0.54782974720001221" blue="0.62907493114471436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <nil key="highlightedColor"/>
                                    </label>
                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Duration: 1" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xk2-sC-g25">
                                        <fontDescription key="fontDescription" name="Avenir-Heavy" family="Avenir" pointSize="12"/>
                                        <color key="textColor" red="0.51576060056686401" green="0.54782974720001221" blue="0.62907493114471436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <nil key="highlightedColor"/>
                                    </label>
                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Delay: 0" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="nfx-rk-xQP">
                                        <fontDescription key="fontDescription" name="Avenir-Heavy" family="Avenir" pointSize="12"/>
                                        <color key="textColor" red="0.51576060056686401" green="0.54782974720001221" blue="0.62907493114471436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <nil key="highlightedColor"/>
                                    </label>
                                </subviews>
                                <color key="backgroundColor" red="0.92941176469999998" green="0.94509803920000002" blue="0.96078431369999995" alpha="0.5" colorSpace="custom" customColorSpace="sRGB"/>
                                <constraints>
                                    <constraint firstAttribute="bottom" secondItem="afX-4m-2dp" secondAttribute="bottom" id="1eb-9b-h6d"/>
                                    <constraint firstItem="afX-4m-2dp" firstAttribute="leading" secondItem="Mpa-80-hgk" secondAttribute="leading" id="1jm-Ss-jJD"/>
                                    <constraint firstAttribute="trailing" secondItem="8lZ-4r-F62" secondAttribute="trailing" constant="15" id="3og-cZ-28y"/>
                                    <constraint firstItem="xk2-sC-g25" firstAttribute="centerX" secondItem="AkI-y0-NKD" secondAttribute="centerX" constant="0.5" id="4D1-92-tWb"/>
                                    <constraint firstItem="afX-4m-2dp" firstAttribute="top" secondItem="8lZ-4r-F62" secondAttribute="bottom" constant="2" id="82c-B6-4CV"/>
                                    <constraint firstItem="afX-4m-2dp" firstAttribute="top" secondItem="Vkq-iS-03i" secondAttribute="bottom" constant="2" id="P5Q-Q6-k4L"/>
                                    <constraint firstItem="hZO-3H-UCU" firstAttribute="centerX" secondItem="Vkq-iS-03i" secondAttribute="centerX" constant="0.5" id="Rc3-TA-Mlh"/>
                                    <constraint firstItem="xk2-sC-g25" firstAttribute="top" secondItem="AkI-y0-NKD" secondAttribute="bottom" constant="7" id="aAF-gG-1hQ"/>
                                    <constraint firstItem="nfx-rk-xQP" firstAttribute="top" secondItem="8lZ-4r-F62" secondAttribute="bottom" constant="7" id="da6-8l-41R"/>
                                    <constraint firstAttribute="trailing" secondItem="afX-4m-2dp" secondAttribute="trailing" id="h4J-qO-8O7"/>
                                    <constraint firstItem="hZO-3H-UCU" firstAttribute="top" secondItem="Vkq-iS-03i" secondAttribute="bottom" constant="7" id="hue-dv-VWO"/>
                                    <constraint firstItem="afX-4m-2dp" firstAttribute="top" secondItem="AkI-y0-NKD" secondAttribute="bottom" constant="2" id="kJF-fX-DIF"/>
                                    <constraint firstItem="nfx-rk-xQP" firstAttribute="centerX" secondItem="8lZ-4r-F62" secondAttribute="centerX" constant="0.5" id="n6M-q2-55u"/>
                                    <constraint firstItem="afX-4m-2dp" firstAttribute="centerX" secondItem="AkI-y0-NKD" secondAttribute="centerX" id="uro-Gp-IG5"/>
                                    <constraint firstItem="Vkq-iS-03i" firstAttribute="leading" secondItem="Mpa-80-hgk" secondAttribute="leading" constant="15" id="xBb-q1-Qxw"/>
                                </constraints>
                            </view>
                        </subviews>
                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstAttribute="bottom" secondItem="jEx-iK-Wpm" secondAttribute="bottom" constant="260" id="9YW-cY-h2z"/>
                            <constraint firstItem="Mpa-80-hgk" firstAttribute="top" secondItem="jEx-iK-Wpm" secondAttribute="bottom" constant="8" id="AF7-U1-JtU"/>
                            <constraint firstItem="Mpa-80-hgk" firstAttribute="leading" secondItem="PVY-HA-UuV" secondAttribute="leading" id="F9O-Ei-pyO"/>
                            <constraint firstItem="jEx-iK-Wpm" firstAttribute="leading" secondItem="PVY-HA-UuV" secondAttribute="leading" id="FI0-bs-M2X"/>
                            <constraint firstItem="Mpa-80-hgk" firstAttribute="top" secondItem="jEx-iK-Wpm" secondAttribute="bottom" id="Hdp-A4-BqA"/>
                            <constraint firstAttribute="trailing" secondItem="Mpa-80-hgk" secondAttribute="trailing" id="IgS-mO-S1v"/>
                            <constraint firstItem="Mpa-80-hgk" firstAttribute="top" secondItem="jEx-iK-Wpm" secondAttribute="bottom" constant="8" id="Nec-Vy-qhb"/>
                            <constraint firstItem="Mpa-80-hgk" firstAttribute="top" secondItem="PVY-HA-UuV" secondAttribute="top" constant="340" id="V3b-SI-YOg"/>
                            <constraint firstItem="9I4-Bi-XrU" firstAttribute="top" secondItem="Mpa-80-hgk" secondAttribute="bottom" id="jgt-a7-2k4"/>
                            <constraint firstAttribute="trailing" secondItem="jEx-iK-Wpm" secondAttribute="trailing" id="lOm-Wk-tqa"/>
                            <constraint firstItem="jEx-iK-Wpm" firstAttribute="top" secondItem="PVY-HA-UuV" secondAttribute="top" id="xmN-hg-zCQ"/>
                        </constraints>
                        <variation key="default">
                            <mask key="constraints">
                                <exclude reference="AF7-U1-JtU"/>
                                <exclude reference="Nec-Vy-qhb"/>
                                <exclude reference="V3b-SI-YOg"/>
                            </mask>
                        </variation>
                    </view>
                    <connections>
                        <outlet property="animationPicker" destination="afX-4m-2dp" id="SdP-G4-1Oj"/>
                        <outlet property="ballView" destination="pm9-y7-HlR" id="u3S-vQ-pY3"/>
                        <outlet property="delayLabel" destination="nfx-rk-xQP" id="Esx-IV-Sb1"/>
                        <outlet property="delaySlider" destination="8lZ-4r-F62" id="OPI-qd-973"/>
                        <outlet property="durationLabel" destination="xk2-sC-g25" id="DhZ-TW-o9N"/>
                        <outlet property="durationSlider" destination="AkI-y0-NKD" id="4gv-7c-JV5"/>
                        <outlet property="forceLabel" destination="hZO-3H-UCU" id="ce1-VP-A6b"/>
                        <outlet property="forceSlider" destination="Vkq-iS-03i" id="OcW-56-47O"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="2wM-Df-GyY" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="1111" y="315"/>
        </scene>
        <!--Code View Controller-->
        <scene sceneID="FIC-8l-Zvm">
            <objects>
                <viewController id="gI9-MU-NP6" customClass="CodeViewController" customModule="SpringApp" customModuleProvider="target" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="Cdh-iV-bjE"/>
                        <viewControllerLayoutGuide type="bottom" id="Gnh-dh-azC"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="diL-kH-pKD">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="WDl-Yq-pxG">
                                <state key="normal">
                                    <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                </state>
                                <connections>
                                    <action selector="closeButtonPressed:" destination="gI9-MU-NP6" eventType="touchUpInside" id="6gZ-Qa-jTE"/>
                                </connections>
                            </button>
                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="w0U-Rm-19b" customClass="SpringView" customModule="Spring">
                                <subviews>
                                    <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" text="layer.animation = &quot;shake&quot;" translatesAutoresizingMaskIntoConstraints="NO" id="QYe-M8-GcU">
                                        <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
                                        <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <fontDescription key="fontDescription" name="Menlo-Regular" family="Menlo" pointSize="14"/>
                                        <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
                                    </textView>
                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Code" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jUx-Gp-qoT">
                                        <fontDescription key="fontDescription" name="AvenirNext-Regular" family="Avenir Next" pointSize="20"/>
                                        <color key="textColor" red="0.51576060056686401" green="0.54782974720001221" blue="0.62907493114471436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <nil key="highlightedColor"/>
                                    </label>
                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="yhW-Bx-O4o">
                                        <color key="backgroundColor" red="0.23921568630000001" green="0.25882352939999997" blue="0.30588235289999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                    </view>
                                </subviews>
                                <color key="backgroundColor" red="0.23921568630000001" green="0.25882352939999997" blue="0.30588235289999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                <constraints>
                                    <constraint firstItem="yhW-Bx-O4o" firstAttribute="top" secondItem="w0U-Rm-19b" secondAttribute="top" id="3uX-fZ-rIk"/>
                                    <constraint firstItem="QYe-M8-GcU" firstAttribute="leading" secondItem="w0U-Rm-19b" secondAttribute="leading" constant="10" id="6Im-9V-IYN"/>
                                    <constraint firstItem="jUx-Gp-qoT" firstAttribute="top" secondItem="w0U-Rm-19b" secondAttribute="top" constant="30" id="8eK-QR-S7b"/>
                                    <constraint firstItem="yhW-Bx-O4o" firstAttribute="leading" secondItem="w0U-Rm-19b" secondAttribute="leading" constant="-290" id="E9e-hR-rvz"/>
                                    <constraint firstAttribute="bottom" secondItem="yhW-Bx-O4o" secondAttribute="bottom" id="WWv-SK-HA2"/>
                                    <constraint firstAttribute="bottom" secondItem="QYe-M8-GcU" secondAttribute="bottom" constant="10" id="bBK-7i-q1R"/>
                                    <constraint firstItem="QYe-M8-GcU" firstAttribute="leading" secondItem="yhW-Bx-O4o" secondAttribute="trailing" id="cgV-59-aWe"/>
                                    <constraint firstAttribute="width" constant="300" id="jiZ-9d-R1i"/>
                                    <constraint firstAttribute="centerX" secondItem="jUx-Gp-qoT" secondAttribute="centerX" id="nL7-wh-det"/>
                                    <constraint firstAttribute="trailing" secondItem="QYe-M8-GcU" secondAttribute="trailing" constant="10" id="sKf-sj-Qcr"/>
                                    <constraint firstItem="QYe-M8-GcU" firstAttribute="top" secondItem="jUx-Gp-qoT" secondAttribute="bottom" constant="8.5" id="xVO-L4-qKb"/>
                                </constraints>
                                <userDefinedRuntimeAttributes>
                                    <userDefinedRuntimeAttribute type="string" keyPath="animation" value="squeezeRight"/>
                                    <userDefinedRuntimeAttribute type="boolean" keyPath="animateFrom" value="YES"/>
                                </userDefinedRuntimeAttributes>
                            </view>
                        </subviews>
                        <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstItem="WDl-Yq-pxG" firstAttribute="top" secondItem="diL-kH-pKD" secondAttribute="top" id="EAX-h3-Mmw"/>
                            <constraint firstItem="Gnh-dh-azC" firstAttribute="top" secondItem="WDl-Yq-pxG" secondAttribute="bottom" id="I6b-5N-cjN"/>
                            <constraint firstItem="w0U-Rm-19b" firstAttribute="leading" secondItem="diL-kH-pKD" secondAttribute="leading" id="JVt-bO-12t"/>
                            <constraint firstItem="WDl-Yq-pxG" firstAttribute="leading" secondItem="w0U-Rm-19b" secondAttribute="trailing" id="PiW-m2-KZ8"/>
                            <constraint firstItem="w0U-Rm-19b" firstAttribute="top" secondItem="diL-kH-pKD" secondAttribute="top" id="mhd-BH-PNx"/>
                            <constraint firstAttribute="trailing" secondItem="WDl-Yq-pxG" secondAttribute="trailing" id="x5w-F3-2yX"/>
                            <constraint firstItem="Gnh-dh-azC" firstAttribute="top" secondItem="w0U-Rm-19b" secondAttribute="bottom" id="zzE-Hu-zJ4"/>
                        </constraints>
                    </view>
                    <connections>
                        <outlet property="codeTextView" destination="QYe-M8-GcU" id="9mQ-DZ-Ybr"/>
                        <outlet property="modalView" destination="w0U-Rm-19b" id="eix-Ci-v0v"/>
                        <outlet property="titleLabel" destination="jUx-Gp-qoT" id="f9i-4D-BGh"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="CB3-m9-O4j" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="1827" y="315"/>
        </scene>
        <!--Options View Controller-->
        <scene sceneID="wF4-oh-fMB">
            <objects>
                <viewController id="TLI-qL-rpM" customClass="OptionsViewController" customModule="SpringApp" customModuleProvider="target" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="UWe-6I-A49"/>
                        <viewControllerLayoutGuide type="bottom" id="SLM-Nn-1rU"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="GfF-r4-vRP">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5Zg-Sj-2iX">
                                <state key="normal">
                                    <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                </state>
                                <connections>
                                    <action selector="closeButtonPressed:" destination="TLI-qL-rpM" eventType="touchUpInside" id="Ezy-PN-qV0"/>
                                </connections>
                            </button>
                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lGB-5Y-C2z" customClass="SpringView" customModule="Spring">
                                <subviews>
                                    <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.69999998807907104" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="EqA-ng-ZaN">
                                        <constraints>
                                            <constraint firstAttribute="width" constant="86" id="K9k-UU-sai"/>
                                            <constraint firstAttribute="height" constant="30" id="lyn-rN-SRs"/>
                                        </constraints>
                                        <color key="minimumTrackTintColor" red="0.15340511500835419" green="0.61420810222625732" blue="0.9250943660736084" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <color key="maximumTrackTintColor" red="1" green="1" blue="1" alpha="0.20000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
                                        <connections>
                                            <action selector="dampingSliderChanged:" destination="TLI-qL-rpM" eventType="valueChanged" id="FJS-Wh-VOq"/>
                                            <action selector="forceSliderChanged:" destination="uF9-EU-g6i" eventType="valueChanged" id="A03-fO-WLN"/>
                                        </connections>
                                    </slider>
                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Damping: 0.7" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rM7-dW-gD1">
                                        <fontDescription key="fontDescription" name="AvenirNext-DemiBold" family="Avenir Next" pointSize="12"/>
                                        <color key="textColor" red="0.51576060056686401" green="0.54782974720001221" blue="0.62907493114471436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <nil key="highlightedColor"/>
                                    </label>
                                    <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.69999998807907104" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="P2p-U0-U48">
                                        <constraints>
                                            <constraint firstAttribute="width" constant="86" id="BzJ-vp-Nar"/>
                                            <constraint firstAttribute="height" constant="30" id="bUz-7g-C7D"/>
                                        </constraints>
                                        <color key="minimumTrackTintColor" red="0.15340511500835419" green="0.61420810222625732" blue="0.9250943660736084" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <color key="maximumTrackTintColor" red="1" green="1" blue="1" alpha="0.20000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
                                        <connections>
                                            <action selector="forceSliderChanged:" destination="uF9-EU-g6i" eventType="valueChanged" id="QKl-4V-Gsm"/>
                                            <action selector="velocitySliderChanged:" destination="TLI-qL-rpM" eventType="valueChanged" id="sHd-fw-2K6"/>
                                        </connections>
                                    </slider>
                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Velocity: 0.7" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kqf-ZO-435">
                                        <fontDescription key="fontDescription" name="AvenirNext-DemiBold" family="Avenir Next" pointSize="12"/>
                                        <color key="textColor" red="0.51576060056686401" green="0.54782974720001221" blue="0.62907493114471436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <nil key="highlightedColor"/>
                                    </label>
                                    <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="5" translatesAutoresizingMaskIntoConstraints="NO" id="hp3-5D-ajZ">
                                        <constraints>
                                            <constraint firstAttribute="width" constant="86" id="3wR-k9-xTa"/>
                                            <constraint firstAttribute="height" constant="30" id="4px-bW-89E"/>
                                        </constraints>
                                        <color key="minimumTrackTintColor" red="0.15340511500835419" green="0.61420810222625732" blue="0.9250943660736084" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <color key="maximumTrackTintColor" red="1" green="1" blue="1" alpha="0.20000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
                                        <connections>
                                            <action selector="forceSliderChanged:" destination="uF9-EU-g6i" eventType="valueChanged" id="KHU-DR-Wey"/>
                                            <action selector="rotateSliderChanged:" destination="TLI-qL-rpM" eventType="valueChanged" id="TcI-R0-THJ"/>
                                        </connections>
                                    </slider>
                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Rotate: 0" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Sss-g7-hcA">
                                        <fontDescription key="fontDescription" name="AvenirNext-DemiBold" family="Avenir Next" pointSize="12"/>
                                        <color key="textColor" red="0.51576060056686401" green="0.54782974720001221" blue="0.62907493114471436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <nil key="highlightedColor"/>
                                    </label>
                                    <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="300" translatesAutoresizingMaskIntoConstraints="NO" id="tuU-kH-fxY">
                                        <constraints>
                                            <constraint firstAttribute="width" constant="86" id="00T-Tj-z8w"/>
                                            <constraint firstAttribute="height" constant="30" id="L5G-Cm-PKD"/>
                                        </constraints>
                                        <color key="minimumTrackTintColor" red="0.15340511500835419" green="0.61420810222625732" blue="0.9250943660736084" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <color key="maximumTrackTintColor" red="1" green="1" blue="1" alpha="0.20000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
                                        <connections>
                                            <action selector="forceSliderChanged:" destination="uF9-EU-g6i" eventType="valueChanged" id="lgY-L0-IiW"/>
                                            <action selector="xSliderChanged:" destination="TLI-qL-rpM" eventType="valueChanged" id="Scg-AE-c0n"/>
                                        </connections>
                                    </slider>
                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="x: 0" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8i2-8d-Gyo">
                                        <fontDescription key="fontDescription" name="AvenirNext-DemiBold" family="Avenir Next" pointSize="12"/>
                                        <color key="textColor" red="0.51576060056686401" green="0.54782974720001221" blue="0.62907493114471436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <nil key="highlightedColor"/>
                                    </label>
                                    <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="300" translatesAutoresizingMaskIntoConstraints="NO" id="WeN-Ux-Pdh">
                                        <constraints>
                                            <constraint firstAttribute="width" constant="86" id="Fq8-dY-J5H"/>
                                            <constraint firstAttribute="height" constant="30" id="Xil-lQ-Ii6"/>
                                        </constraints>
                                        <color key="minimumTrackTintColor" red="0.15340511500835419" green="0.61420810222625732" blue="0.9250943660736084" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <color key="maximumTrackTintColor" red="1" green="1" blue="1" alpha="0.20000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
                                        <connections>
                                            <action selector="forceSliderChanged:" destination="uF9-EU-g6i" eventType="valueChanged" id="ziv-DU-lLU"/>
                                            <action selector="ySliderChanged:" destination="TLI-qL-rpM" eventType="valueChanged" id="5lY-h3-wEB"/>
                                        </connections>
                                    </slider>
                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="y: 0" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8kG-Ub-7Gx">
                                        <fontDescription key="fontDescription" name="AvenirNext-DemiBold" family="Avenir Next" pointSize="12"/>
                                        <color key="textColor" red="0.51576060056686401" green="0.54782974720001221" blue="0.62907493114471436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <nil key="highlightedColor"/>
                                    </label>
                                    <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="1" maxValue="5" translatesAutoresizingMaskIntoConstraints="NO" id="6UY-eF-UAm">
                                        <constraints>
                                            <constraint firstAttribute="height" constant="30" id="3uC-E3-FRl"/>
                                            <constraint firstAttribute="width" constant="86" id="b1q-Wz-FNX"/>
                                        </constraints>
                                        <color key="minimumTrackTintColor" red="0.15340511500835419" green="0.61420810222625732" blue="0.9250943660736084" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <color key="maximumTrackTintColor" red="1" green="1" blue="1" alpha="0.20000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
                                        <connections>
                                            <action selector="forceSliderChanged:" destination="uF9-EU-g6i" eventType="valueChanged" id="oqi-mD-dZS"/>
                                            <action selector="scaleSliderChanged:" destination="TLI-qL-rpM" eventType="valueChanged" id="EF6-fQ-SYl"/>
                                        </connections>
                                    </slider>
                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Scale: 1" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="PFw-Jb-2Xm">
                                        <fontDescription key="fontDescription" name="AvenirNext-DemiBold" family="Avenir Next" pointSize="12"/>
                                        <color key="textColor" red="0.51576060056686401" green="0.54782974720001221" blue="0.62907493114471436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <nil key="highlightedColor"/>
                                    </label>
                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="WbE-Ar-bLf" customClass="DesignableButton" customModule="Spring">
                                        <constraints>
                                            <constraint firstAttribute="height" constant="36" id="Gt6-ig-db1"/>
                                            <constraint firstAttribute="width" constant="100" id="oBI-wy-nzi"/>
                                        </constraints>
                                        <fontDescription key="fontDescription" name="AvenirNext-DemiBold" family="Avenir Next" pointSize="16"/>
                                        <state key="normal" title="Reset">
                                            <color key="titleColor" red="0.51576060056686401" green="0.54782974720001221" blue="0.62907493114471436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                            <color key="titleShadowColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
                                        </state>
                                        <userDefinedRuntimeAttributes>
                                            <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
                                                <real key="value" value="3"/>
                                            </userDefinedRuntimeAttribute>
                                            <userDefinedRuntimeAttribute type="number" keyPath="borderWidth">
                                                <real key="value" value="1"/>
                                            </userDefinedRuntimeAttribute>
                                            <userDefinedRuntimeAttribute type="color" keyPath="borderColor">
                                                <color key="value" red="0.51576060056686401" green="0.54782974720001221" blue="0.62907493114471436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                            </userDefinedRuntimeAttribute>
                                        </userDefinedRuntimeAttributes>
                                        <connections>
                                            <action selector="resetButtonPressed:" destination="TLI-qL-rpM" eventType="touchUpInside" id="0cE-Y8-6dl"/>
                                        </connections>
                                    </button>
                                </subviews>
                                <color key="backgroundColor" red="0.23921568630000001" green="0.25882352939999997" blue="0.30588235289999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                <constraints>
                                    <constraint firstAttribute="centerX" secondItem="WbE-Ar-bLf" secondAttribute="centerX" id="3q2-yh-Ybl"/>
                                    <constraint firstItem="EqA-ng-ZaN" firstAttribute="top" secondItem="lGB-5Y-C2z" secondAttribute="top" constant="35" id="6Qu-N6-4Gs"/>
                                    <constraint firstItem="Sss-g7-hcA" firstAttribute="top" secondItem="hp3-5D-ajZ" secondAttribute="bottom" constant="6.5" id="7fN-2B-svp"/>
                                    <constraint firstItem="WbE-Ar-bLf" firstAttribute="top" secondItem="8kG-Ub-7Gx" secondAttribute="bottom" constant="29" id="7pX-za-V55"/>
                                    <constraint firstItem="kqf-ZO-435" firstAttribute="centerX" secondItem="P2p-U0-U48" secondAttribute="centerX" constant="0.5" id="89v-Cf-WeC"/>
                                    <constraint firstItem="6UY-eF-UAm" firstAttribute="top" secondItem="hp3-5D-ajZ" secondAttribute="bottom" constant="51" id="93Z-4G-cdy"/>
                                    <constraint firstAttribute="centerX" secondItem="WeN-Ux-Pdh" secondAttribute="centerX" id="A0L-HO-nic"/>
                                    <constraint firstItem="tuU-kH-fxY" firstAttribute="leading" secondItem="lGB-5Y-C2z" secondAttribute="leading" constant="15" id="H7r-N1-b8C"/>
                                    <constraint firstItem="8kG-Ub-7Gx" firstAttribute="top" secondItem="WeN-Ux-Pdh" secondAttribute="bottom" constant="6.5" id="KrF-gs-5oU"/>
                                    <constraint firstItem="WeN-Ux-Pdh" firstAttribute="centerX" secondItem="8kG-Ub-7Gx" secondAttribute="centerX" id="Mdc-2s-1pc"/>
                                    <constraint firstItem="PFw-Jb-2Xm" firstAttribute="top" secondItem="6UY-eF-UAm" secondAttribute="bottom" constant="6" id="QO0-Wz-vcZ"/>
                                    <constraint firstItem="tuU-kH-fxY" firstAttribute="top" secondItem="EqA-ng-ZaN" secondAttribute="bottom" constant="51" id="SIY-9j-OjG"/>
                                    <constraint firstAttribute="trailing" secondItem="6UY-eF-UAm" secondAttribute="trailing" constant="15" id="SNb-A0-4Ni"/>
                                    <constraint firstAttribute="height" constant="360" id="Thv-hf-bgU"/>
                                    <constraint firstItem="8i2-8d-Gyo" firstAttribute="top" secondItem="tuU-kH-fxY" secondAttribute="bottom" constant="6.5" id="Z6g-Pg-J9Z"/>
                                    <constraint firstItem="kqf-ZO-435" firstAttribute="top" secondItem="P2p-U0-U48" secondAttribute="bottom" constant="6.5" id="cD1-0G-GxK"/>
                                    <constraint firstItem="P2p-U0-U48" firstAttribute="top" secondItem="lGB-5Y-C2z" secondAttribute="top" constant="35" id="fim-re-Tvs"/>
                                    <constraint firstItem="rM7-dW-gD1" firstAttribute="top" secondItem="EqA-ng-ZaN" secondAttribute="bottom" constant="7" id="gy4-M2-2a8"/>
                                    <constraint firstItem="rM7-dW-gD1" firstAttribute="centerX" secondItem="EqA-ng-ZaN" secondAttribute="centerX" id="iMG-zU-bVk"/>
                                    <constraint firstItem="WeN-Ux-Pdh" firstAttribute="top" secondItem="P2p-U0-U48" secondAttribute="bottom" constant="51" id="ibF-Yl-SDb"/>
                                    <constraint firstAttribute="trailing" secondItem="hp3-5D-ajZ" secondAttribute="trailing" constant="15" id="k7t-l0-jvZ"/>
                                    <constraint firstAttribute="centerX" secondItem="P2p-U0-U48" secondAttribute="centerX" id="lnz-n5-rkT"/>
                                    <constraint firstItem="PFw-Jb-2Xm" firstAttribute="centerX" secondItem="6UY-eF-UAm" secondAttribute="centerX" id="pon-o7-o2z"/>
                                    <constraint firstItem="tuU-kH-fxY" firstAttribute="centerX" secondItem="8i2-8d-Gyo" secondAttribute="centerX" id="qCh-xs-aLi"/>
                                    <constraint firstItem="Sss-g7-hcA" firstAttribute="centerX" secondItem="hp3-5D-ajZ" secondAttribute="centerX" constant="0.5" id="rg4-by-E5B"/>
                                    <constraint firstItem="hp3-5D-ajZ" firstAttribute="top" secondItem="lGB-5Y-C2z" secondAttribute="top" constant="35" id="tI2-8f-ZEE"/>
                                    <constraint firstItem="EqA-ng-ZaN" firstAttribute="leading" secondItem="lGB-5Y-C2z" secondAttribute="leading" constant="15" id="uBy-dJ-VGW"/>
                                </constraints>
                                <userDefinedRuntimeAttributes>
                                    <userDefinedRuntimeAttribute type="string" keyPath="animation" value="squeezeUp"/>
                                    <userDefinedRuntimeAttribute type="boolean" keyPath="animateFrom" value="YES"/>
                                </userDefinedRuntimeAttributes>
                            </view>
                        </subviews>
                        <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstItem="SLM-Nn-1rU" firstAttribute="top" secondItem="lGB-5Y-C2z" secondAttribute="bottom" constant="-100" id="5iG-ce-9Po"/>
                            <constraint firstItem="5Zg-Sj-2iX" firstAttribute="leading" secondItem="GfF-r4-vRP" secondAttribute="leading" id="U08-9E-ZGK"/>
                            <constraint firstItem="lGB-5Y-C2z" firstAttribute="top" secondItem="5Zg-Sj-2iX" secondAttribute="bottom" id="eag-fC-BEO"/>
                            <constraint firstItem="5Zg-Sj-2iX" firstAttribute="top" secondItem="GfF-r4-vRP" secondAttribute="top" id="fKp-a5-cLf"/>
                            <constraint firstAttribute="trailing" secondItem="5Zg-Sj-2iX" secondAttribute="trailing" id="hPv-87-I93"/>
                            <constraint firstAttribute="trailing" secondItem="lGB-5Y-C2z" secondAttribute="trailing" id="o6k-Mh-t9x"/>
                            <constraint firstItem="lGB-5Y-C2z" firstAttribute="leading" secondItem="GfF-r4-vRP" secondAttribute="leading" id="qAZ-W2-lzb"/>
                        </constraints>
                    </view>
                    <connections>
                        <outlet property="dampingLabel" destination="rM7-dW-gD1" id="wH0-Uk-5ml"/>
                        <outlet property="dampingSlider" destination="EqA-ng-ZaN" id="5WY-8t-rzX"/>
                        <outlet property="modalView" destination="lGB-5Y-C2z" id="wMU-Bs-mMh"/>
                        <outlet property="rotateLabel" destination="Sss-g7-hcA" id="NL3-4o-32s"/>
                        <outlet property="rotateSlider" destination="hp3-5D-ajZ" id="8Go-do-2bg"/>
                        <outlet property="scaleLabel" destination="PFw-Jb-2Xm" id="xmu-OW-WwZ"/>
                        <outlet property="scaleSlider" destination="6UY-eF-UAm" id="hIu-5f-bS4"/>
                        <outlet property="velocityLabel" destination="kqf-ZO-435" id="w64-JN-1fH"/>
                        <outlet property="velocitySlider" destination="P2p-U0-U48" id="Mar-PQ-YjB"/>
                        <outlet property="xLabel" destination="8i2-8d-Gyo" id="G5H-7A-fFb"/>
                        <outlet property="xSlider" destination="tuU-kH-fxY" id="prF-yU-WXj"/>
                        <outlet property="yLabel" destination="8kG-Ub-7Gx" id="goX-4s-lbS"/>
                        <outlet property="ySlider" destination="WeN-Ux-Pdh" id="HdM-do-VcM"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="WF7-yJ-qnW" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="1111" y="1028"/>
        </scene>
    </scenes>
    <resources>
        <image name="icon-shape" width="30" height="13"/>
    </resources>
</document>


================================================
FILE: SpringApp/CodeViewController.swift
================================================
//
//  CodeViewController.swift
//  DesignerNewsApp
//
//  Created by Meng To on 2015-01-05.
//  Copyright (c) 2015 Meng To. All rights reserved.
//

import UIKit
import Spring

class CodeViewController: UIViewController {

    @IBOutlet weak var modalView: SpringView!
    @IBOutlet weak var codeTextView: UITextView!
    @IBOutlet weak var titleLabel: UILabel!
    var codeText: String = ""
    var data: SpringView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        modalView.transform = CGAffineTransform(translationX: -300, y: 0)
        
        if data.animation != "" {
            codeText += "layer.animation = \"\(data.animation)\"\n"
        }
        if data.curve != "" {
            codeText += "layer.curve = \"\(data.curve)\"\n"
        }
        if data.force != 1 {
            codeText += String(format: "layer.force =  %.1f\n", Double(data.force))
        }
        if data.duration != 0.7 {
            codeText += String(format: "layer.duration =  %.1f\n", Double(data.duration))
        }
        if data.delay != 0 {
            codeText += String(format: "layer.delay =  %.1f\n", Double(data.delay))
        }
        if data.scaleX != 1 {
            codeText += String(format: "layer.scaleX =  %.1f\n", Double(data.scaleX))
        }
        if data.scaleY != 1 {
            codeText += String(format: "layer.scaleY =  %.1f\n", Double(data.scaleY))
        }
        if data.rotate != 0 {
            codeText += String(format: "layer.rotate =  %.1f\n", Double(data.rotate))
        }
        if data.damping != 0.7 {
            codeText += String(format: "layer.damping =  %.1f\n", Double(data.damping))
        }
        if data.velocity != 0.7 {
            codeText += String(format: "layer.velocity =  %.1f\n", Double(data.velocity))
        }
        codeText += "layer.animate()"
        
        codeTextView.text = codeText
    }
    
    @IBAction func closeButtonPressed(_ sender: AnyObject) {
        UIApplication.shared.sendAction(#selector(SpringViewController.maximizeView(_:)), to: nil, from: self, for: nil)
        
        modalView.animation = "slideRight"
        modalView.animateFrom = false
        modalView.animateToNext(completion: {
            self.dismiss(animated: false, completion: nil)
        })
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(true)
        
        modalView.animate()
        
        UIApplication.shared.sendAction(#selector(SpringViewController.minimizeView(_:)), to: nil, from: self, for: nil)
    }
}




================================================
FILE: SpringApp/Images.xcassets/AppIcon.appiconset/Contents.json
================================================
{
  "images" : [
    {
      "size" : "29x29",
      "idiom" : "iphone",
      "filename" : "appicon@58.png",
      "scale" : "2x"
    },
    {
      "size" : "29x29",
      "idiom" : "iphone",
      "filename" : "appicon@87.png",
      "scale" : "3x"
    },
    {
      "size" : "40x40",
      "idiom" : "iphone",
      "filename" : "appicon@80.png",
      "scale" : "2x"
    },
    {
      "size" : "40x40",
      "idiom" : "iphone",
      "filename" : "appicon@120.png",
      "scale" : "3x"
    },
    {
      "size" : "60x60",
      "idiom" : "iphone",
      "filename" : "appicon@120-1.png",
      "scale" : "2x"
    },
    {
      "size" : "60x60",
      "idiom" : "iphone",
      "filename" : "appicon@180.png",
      "scale" : "3x"
    },
    {
      "size" : "29x29",
      "idiom" : "ipad",
      "filename" : "appicon@29.png",
      "scale" : "1x"
    },
    {
      "size" : "29x29",
      "idiom" : "ipad",
      "filename" : "appicon@58-1.png",
      "scale" : "2x"
    },
    {
      "size" : "40x40",
      "idiom" : "ipad",
      "filename" : "appicon@40.png",
      "scale" : "1x"
    },
    {
      "size" : "40x40",
      "idiom" : "ipad",
      "filename" : "appicon@80-1.png",
      "scale" : "2x"
    },
    {
      "size" : "76x76",
      "idiom" : "ipad",
      "filename" : "appicon@76.png",
      "scale" : "1x"
    },
    {
      "size" : "76x76",
      "idiom" : "ipad",
      "filename" : "appicon@152.png",
      "scale" : "2x"
    },
    {
      "size" : "83.5x83.5",
      "idiom" : "ipad",
      "filename" : "appicon@167.png",
      "scale" : "2x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

================================================
FILE: SpringApp/Images.xcassets/icon-shape.imageset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "universal",
      "filename" : "icon-shape.pdf"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

================================================
FILE: SpringApp/Images.xcassets/loading.imageset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "universal",
      "filename" : "loading.pdf"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

================================================
FILE: SpringApp/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>Spring</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>1</string>
	<key>LSRequiresIPhoneOS</key>
	<true/>
	<key>UILaunchStoryboardName</key>
	<string>LaunchScreen</string>
	<key>UIMainStoryboardFile</key>
	<string>Main</string>
	<key>UIRequiredDeviceCapabilities</key>
	<array>
		<string>armv7</string>
	</array>
	<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>UIViewControllerBasedStatusBarAppearance</key>
	<false/>
</dict>
</plist>


================================================
FILE: SpringApp/OptionsViewController.swift
================================================
//
//  OptionsViewController.swift
//  DesignerNewsApp
//
//  Created by Meng To on 2015-01-04.
//  Copyright (c) 2015 Meng To. All rights reserved.
//

import UIKit
import Spring

protocol OptionsViewControllerDelegate: class {
    func dampingSliderChanged(_ sender: AnyObject)
    func velocitySliderChanged(_ sender: AnyObject)
    func scaleSliderChanged(_ sender: AnyObject)
    func xSliderChanged(_ sender: AnyObject)
    func ySliderChanged(_ sender: AnyObject)
    func rotateSliderChanged(_ sender: AnyObject)
    func resetButtonPressed(_ sender: AnyObject)
}

class OptionsViewController: UIViewController {
    
    @IBOutlet weak var modalView: SpringView!
    
    @IBOutlet weak var dampingLabel: UILabel!
    @IBOutlet weak var velocityLabel: UILabel!
    @IBOutlet weak var scaleLabel: UILabel!
    @IBOutlet weak var xLabel: UILabel!
    @IBOutlet weak var yLabel: UILabel!
    @IBOutlet weak var rotateLabel: UILabel!
    
    @IBOutlet weak var dampingSlider: UISlider!
    @IBOutlet weak var velocitySlider: UISlider!
    @IBOutlet weak var scaleSlider: UISlider!
    @IBOutlet weak var xSlider: UISlider!
    @IBOutlet weak var ySlider: UISlider!
    @IBOutlet weak var rotateSlider: UISlider!
    
    var selectedDamping: CGFloat = 0.7
    var selectedVelocity: CGFloat = 0.7
    var selectedScale: CGFloat = 1
    var selectedX: CGFloat = 0
    var selectedY: CGFloat = 0
    var selectedRotate: CGFloat = 0
    
    weak var delegate: OptionsViewControllerDelegate?
    var data: SpringView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        modalView.transform = CGAffineTransform(translationX: 0, y: 300)
        
        dampingSlider.setValue(Float(data.damping), animated: true)
        velocitySlider.setValue(Float(data.velocity), animated: true)
        scaleSlider.setValue(Float(data.scaleX), animated: true)
        xSlider.setValue(Float(data.x), animated: true)
        ySlider.setValue(Float(data.y), animated: true)
        rotateSlider.setValue(Float(data.rotate), animated: true)
                
        dampingLabel.text = getString("Damping", value: data.damping)
        velocityLabel.text = getString("Velocity", value: data.velocity)
        scaleLabel.text = getString("Scale", value: data.scaleX)
        xLabel.text = getString("x", value: data.x)
        yLabel.text = getString("y", value: data.y)
        rotateLabel.text = getString("Rotate", value: data.rotate)
    }

    @IBAction func dampingSliderChanged(_ sender: AnyObject) {
        selectedDamping = sender.value(forKey: "value") as! CGFloat
        delegate?.dampingSliderChanged(sender)
        dampingLabel.text = getString("Damping", value: selectedDamping)
    }
    
    @IBAction func velocitySliderChanged(_ sender: AnyObject) {
        selectedVelocity = sender.value(forKey: "value") as! CGFloat
        delegate?.velocitySliderChanged(sender)
        velocityLabel.text = getString("Velocity", value: selectedVelocity)
    }
    
    @IBAction func scaleSliderChanged(_ sender: AnyObject) {
        selectedScale = sender.value(forKey: "value") as! CGFloat
        delegate?.scaleSliderChanged(sender)
        scaleLabel.text = getString("Scale", value: selectedScale)
    }
    
    @IBAction func xSliderChanged(_ sender: AnyObject) {
        selectedX = sender.value(forKey: "value") as! CGFloat
        delegate?.xSliderChanged(sender)
        xLabel.text = getString("X", value: selectedX)
    }
    
    @IBAction func ySliderChanged(_ sender: AnyObject) {
        selectedY = sender.value(forKey: "value") as! CGFloat
        delegate?.ySliderChanged(sender)
        yLabel.text = getString("Y", value: selectedY)
    }
    
    @IBAction func rotateSliderChanged(_ sender: AnyObject) {
        selectedRotate = sender.value(forKey: "value") as! CGFloat
        delegate?.rotateSliderChanged(sender)
        rotateLabel.text = getString("Rotate", value: selectedRotate)
    }
    
    @IBAction func resetButtonPressed(_ sender: AnyObject) {
        delegate?.resetButtonPressed(sender)
        dismiss(animated: true, completion: nil)
        
        UIApplication.shared.sendAction(#selector(SpringViewController.maximizeView(_:)), to: nil, from: self, for: nil)
    }
    
    @IBAction func closeButtonPressed(_ sender: AnyObject) {
        dismiss(animated: true, completion: nil)
        
        UIApplication.shared.sendAction(#selector(SpringViewController.maximizeView(_:)), to: nil, from: self, for: nil)
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(true)
        
        UIApplication.shared.sendAction(#selector(SpringViewController.minimizeView(_:)), to: nil, from: self, for: nil)
        
        modalView.animate()
    }
    
    func getString(_ name: String, value: CGFloat) -> String {
        return String(format: "\(name): %.1f", Double(value))
    }
}


================================================
FILE: SpringApp/SpringViewController.swift
================================================
//
//  SpringViewController.swift
//  DesignerNewsApp
//
//  Created by Meng To on 2015-01-02.
//  Copyright (c) 2015 Meng To. All rights reserved.
//

import UIKit
import Spring

class SpringViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, OptionsViewControllerDelegate {

    @IBOutlet weak var delayLabel: UILabel!
    @IBOutlet weak var durationLabel: UILabel!
    @IBOutlet weak var forceLabel: UILabel!
    @IBOutlet weak var delaySlider: UISlider!
    @IBOutlet weak var durationSlider: UISlider!
    @IBOutlet weak var forceSlider: UISlider!
    @IBOutlet weak var ballView: SpringView!
    @IBOutlet weak var animationPicker: UIPickerView!
    
    var selectedRow: Int = 0
    var selectedEasing: Int = 0
    
    var selectedForce: CGFloat = 1
    var selectedDuration: CGFloat = 1
    var selectedDelay: CGFloat = 0
    
    var selectedDamping: CGFloat = 0.7
    var selectedVelocity: CGFloat = 0.7
    var selectedScale: CGFloat = 1
    var selectedX: CGFloat = 0
    var selectedY: CGFloat = 0
    var selectedRotate: CGFloat = 0
    
    @IBAction func forceSliderChanged(_ sender: AnyObject) {
        selectedForce = sender.value(forKey: "value") as! CGFloat
        animateView()
        forceLabel.text = String(format: "Force: %.1f", Double(selectedForce))
    }
    @IBAction func durationSliderChanged(_ sender: AnyObject) {
        selectedDuration = sender.value(forKey: "value") as! CGFloat
        animateView()
        durationLabel.text = String(format: "Duration: %.1f", Double(selectedDuration))
    }
    @IBAction func delaySliderChanged(_ sender: AnyObject) {
        selectedDelay = sender.value(forKey: "value") as! CGFloat
        animateView()
        delayLabel.text = String(format: "Delay: %.1f", Double(selectedDelay))
    }

    func dampingSliderChanged(_ sender: AnyObject) {
        selectedDamping = sender.value(forKey: "value") as! CGFloat
        animateView()
    }
    
    func velocitySliderChanged(_ sender: AnyObject) {
        selectedVelocity = sender.value(forKey: "value") as! CGFloat
        animateView()
    }
    
    func scaleSliderChanged(_ sender: AnyObject) {
        selectedScale = sender.value(forKey: "value") as! CGFloat
        animateView()
    }
    
    func xSliderChanged(_ sender: AnyObject) {
        selectedX = sender.value(forKey: "value") as! CGFloat
        animateView()
    }
    
    func ySliderChanged(_ sender: AnyObject) {
        selectedY = sender.value(forKey: "value") as! CGFloat
        animateView()
    }
    
    func rotateSliderChanged(_ sender: AnyObject) {
        selectedRotate = sender.value(forKey: "value") as! CGFloat
        animateView()
    }
    
    func animateView() {
        setOptions()
        ballView.animate()
    }
    
    func setOptions() {
        ballView.force = selectedForce
        ballView.duration = selectedDuration
        ballView.delay = selectedDelay
        
        ballView.damping = selectedDamping
        ballView.velocity = selectedVelocity
        ballView.scaleX = selectedScale
        ballView.scaleY = selectedScale
        ballView.x = selectedX
        ballView.y = selectedY
        ballView.rotate = selectedRotate
        
        ballView.animation = animations[selectedRow].rawValue
        ballView.curve = animationCurves[selectedEasing].rawValue
    }
    
   @objc func minimizeView(_ sender: AnyObject) {
        SpringAnimation.spring(duration: 0.7, animations: {
            self.view.transform = CGAffineTransform(scaleX: 0.935, y: 0.935)
        })
        UIApplication.shared.setStatusBarStyle(UIStatusBarStyle.lightContent, animated: true)
    }
    
   @objc func maximizeView(_ sender: AnyObject) {
        SpringAnimation.spring(duration: 0.7, animations: {
            self.view.transform = CGAffineTransform(scaleX: 1, y: 1)
        })
        UIApplication.shared.setStatusBarStyle(UIStatusBarStyle.default, animated: true)
    }

    let animations: [Spring.AnimationPreset] = [
        .Shake,
        .Pop,
        .Morph,
        .Squeeze,
        .Wobble,
        .Swing,
        .FlipX,
        .FlipY,
        .Fall,
        .SqueezeLeft,
        .SqueezeRight,
        .SqueezeDown,
        .SqueezeUp,
        .SlideLeft,
        .SlideRight,
        .SlideDown,
        .SlideUp,
        .FadeIn,
        .FadeOut,
        .FadeInLeft,
        .FadeInRight,
        .FadeInDown,
        .FadeInUp,
        .ZoomIn,
        .ZoomOut,
        .Flash
    ]

    var animationCurves: [Spring.AnimationCurve] = [
        .EaseIn,
        .EaseOut,
        .EaseInOut,
        .Linear,
        .Spring,
        .EaseInSine,
        .EaseOutSine,
        .EaseInOutSine,
        .EaseInQuad,
        .EaseOutQuad,
        .EaseInOutQuad,
        .EaseInCubic,
        .EaseOutCubic,
        .EaseInOutCubic,
        .EaseInQuart,
        .EaseOutQuart,
        .EaseInOutQuart,
        .EaseInQuint,
        .EaseOutQuint,
        .EaseInOutQuint,
        .EaseInExpo,
        .EaseOutExpo,
        .EaseInOutExpo,
        .EaseInCirc,
        .EaseOutCirc,
        .EaseInOutCirc,
        .EaseInBack,
        .EaseOutBack,
        .EaseInOutBack
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        animationPicker.delegate = self
        animationPicker.dataSource = self
        animationPicker.showsSelectionIndicator = true
    }
    
    @IBAction func ballButtonPressed(_ sender: AnyObject) {
        
        UIView.animate(withDuration: 0.1, animations: {
            self.ballView.backgroundColor = UIColor(hex: "69DBFF")
        }, completion: { finished in
            UIView.animate(withDuration: 0.5, animations: {
                self.ballView.backgroundColor = UIColor(hex: "#279CEB")
            })
        })
        
        animateView()
    }
    
    var isBall = false
    func changeBall() {
        isBall = !isBall
        let animation = CABasicAnimation()
        let halfWidth = ballView.frame.width / 2
        let cornerRadius: CGFloat = isBall ? halfWidth : 10
        animation.keyPath = "cornerRadius"
        animation.fromValue = isBall ? 10 : halfWidth
        animation.toValue = cornerRadius
        animation.duration = 0.2
        ballView.layer.cornerRadius = cornerRadius
        ballView.layer.add(animation, forKey: "radius")
    }
    
    @IBAction func shapeButtonPressed(_ sender: AnyObject) {
        changeBall()
    }
    
    func resetButtonPressed(_ sender: AnyObject) {
        selectedForce = 1
        selectedDuration = 1
        selectedDelay = 0
        
        selectedDamping = 0.7
        selectedVelocity = 0.7
        selectedScale = 1
        selectedX = 0
        selectedY = 0
        selectedRotate = 0
        
        forceSlider.setValue(Float(selectedForce), animated: true)
        durationSlider.setValue(Float(selectedDuration), animated: true)
        delaySlider.setValue(Float(selectedDelay), animated: true)
        
        forceLabel.text = String(format: "Force: %.1f", Double(selectedForce))
        durationLabel.text = String(format: "Duration: %.1f", Double(selectedDuration))
        delayLabel.text = String(format: "Delay: %.1f", Double(selectedDelay))
    }
    
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 2
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return component == 0 ? animations.count : animationCurves.count
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return component == 0 ? animations[row].rawValue : animationCurves[row].rawValue
    }
    
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        switch component {
        case 0:
            selectedRow = row
            animateView()
        default:
            selectedEasing = row
            animateView()
        }
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let optionsViewController = segue.destination as? OptionsViewController {
            optionsViewController.delegate = self
            setOptions()
            optionsViewController.data = ballView
        }
        else if let codeViewController = segue.destination as? CodeViewController {
            setOptions()
            codeViewController.data = ballView
        }
    }
}


================================================
FILE: SpringApp.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 46;
	objects = {

/* Begin PBXBuildFile section */
		1A4FDA381A6E447
Download .txt
gitextract_6f9a0zjb/

├── .gitignore
├── .swift-version
├── LICENSE
├── README.md
├── Spring/
│   ├── AsyncButton.swift
│   ├── AsyncImageView.swift
│   ├── AutoTextView.swift
│   ├── BlurView.swift
│   ├── DesignableButton.swift
│   ├── DesignableImageView.swift
│   ├── DesignableLabel.swift
│   ├── DesignableTabBarController.swift
│   ├── DesignableTextField.swift
│   ├── DesignableTextView.swift
│   ├── DesignableView.swift
│   ├── ImageLoader.swift
│   ├── Info.plist
│   ├── KeyboardLayoutConstraint.swift
│   ├── LoadingView.swift
│   ├── LoadingView.xib
│   ├── Misc.swift
│   ├── SoundPlayer.swift
│   ├── Spring.h
│   ├── Spring.swift
│   ├── SpringAnimation.swift
│   ├── SpringButton.swift
│   ├── SpringImageView.swift
│   ├── SpringLabel.swift
│   ├── SpringTextField.swift
│   ├── SpringTextView.swift
│   ├── SpringView.swift
│   ├── TransitionManager.swift
│   ├── TransitionZoom.swift
│   └── UnwindSegue.swift
├── Spring.podspec
├── SpringApp/
│   ├── AppDelegate.swift
│   ├── Base.lproj/
│   │   ├── LaunchScreen.xib
│   │   └── Main.storyboard
│   ├── CodeViewController.swift
│   ├── Images.xcassets/
│   │   ├── AppIcon.appiconset/
│   │   │   └── Contents.json
│   │   ├── icon-shape.imageset/
│   │   │   └── Contents.json
│   │   └── loading.imageset/
│   │       └── Contents.json
│   ├── Info.plist
│   ├── OptionsViewController.swift
│   └── SpringViewController.swift
├── SpringApp.xcodeproj/
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   └── contents.xcworkspacedata
│   └── xcshareddata/
│       └── xcschemes/
│           ├── Spring.xcscheme
│           └── SpringApp.xcscheme
├── SpringAppTests/
│   ├── Info.plist
│   └── SpringAppTests.swift
└── SpringTests/
    ├── Info.plist
    └── SpringTests.swift
Condensed preview — 53 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (265K chars).
[
  {
    "path": ".gitignore",
    "chars": 214,
    "preview": "build/\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspec"
  },
  {
    "path": ".swift-version",
    "chars": 2,
    "preview": "4\n"
  },
  {
    "path": "LICENSE",
    "chars": 1077,
    "preview": "Copyright (c) 2014-2015 Meng To (meng@designcode.io)\n\nPermission is hereby granted, free of charge, to any person obtain"
  },
  {
    "path": "README.md",
    "chars": 2156,
    "preview": "## Updated for Swift 4.2\nRequires Xcode 10 and Swift 4.2.\n\n## Installation\nDrop in the Spring folder to your Xcode proje"
  },
  {
    "path": "Spring/AsyncButton.swift",
    "chars": 2098,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 James Tang (j@jamztang.com)\n//\n// Permission is hereby granted, free o"
  },
  {
    "path": "Spring/AsyncImageView.swift",
    "chars": 2056,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 James Tang (j@jamztang.com)\n//\n// Permission is hereby granted, free o"
  },
  {
    "path": "Spring/AutoTextView.swift",
    "chars": 688,
    "preview": "//\n//  AutoTextView.swift\n//  SpringApp\n//\n//  Created by Meng To on 2015-03-27.\n//  Copyright (c) 2015 Meng To. All rig"
  },
  {
    "path": "Spring/BlurView.swift",
    "chars": 1533,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/DesignableButton.swift",
    "chars": 2272,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/DesignableImageView.swift",
    "chars": 1678,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/DesignableLabel.swift",
    "chars": 2021,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/DesignableTabBarController.swift",
    "chars": 4917,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/DesignableTextField.swift",
    "chars": 3842,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/DesignableTextView.swift",
    "chars": 2505,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/DesignableView.swift",
    "chars": 2271,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/ImageLoader.swift",
    "chars": 3330,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2014 Nate Lyman (https://github.com/natelyman/SwiftImageLoader)\n//\n// Permi"
  },
  {
    "path": "Spring/Info.plist",
    "chars": 806,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Spring/KeyboardLayoutConstraint.swift",
    "chars": 4348,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 James Tang (j@jamztang.com)\n//\n// Permission is hereby granted, free o"
  },
  {
    "path": "Spring/LoadingView.swift",
    "chars": 3016,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/LoadingView.xib",
    "chars": 3238,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" versi"
  },
  {
    "path": "Spring/Misc.swift",
    "chars": 9119,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/SoundPlayer.swift",
    "chars": 2115,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 James Tang (j@jamztang.com)\n//\n// Permission is hereby granted, free o"
  },
  {
    "path": "Spring/Spring.h",
    "chars": 471,
    "preview": "//\n//  Spring.h\n//  Spring\n//\n//  Created by James Tang on 20/1/15.\n//  Copyright (c) 2015 Meng To. All rights reserved."
  },
  {
    "path": "Spring/Spring.swift",
    "chars": 24113,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/SpringAnimation.swift",
    "chars": 3757,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/SpringButton.swift",
    "chars": 2733,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/SpringImageView.swift",
    "chars": 2740,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/SpringLabel.swift",
    "chars": 2732,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/SpringTextField.swift",
    "chars": 2739,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/SpringTextView.swift",
    "chars": 2738,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/SpringView.swift",
    "chars": 2733,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/TransitionManager.swift",
    "chars": 3764,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/TransitionZoom.swift",
    "chars": 3334,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring/UnwindSegue.swift",
    "chars": 1287,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2015 Meng To (meng@designcode.io)\n//\n// Permission is hereby granted, free "
  },
  {
    "path": "Spring.podspec",
    "chars": 577,
    "preview": "Pod::Spec.new do |s|\n  s.name = 'Spring'\n  s.version = '1.0.6'\n  s.license = 'MIT'\n  s.summary = 'A library to simplify "
  },
  {
    "path": "SpringApp/AppDelegate.swift",
    "chars": 2073,
    "preview": "//\n//  AppDelegate.swift\n//  SpringApp\n//\n//  Created by Meng To on 2015-01-06.\n//  Copyright (c) 2015 Meng To. All righ"
  },
  {
    "path": "SpringApp/Base.lproj/LaunchScreen.xib",
    "chars": 3121,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" versi"
  },
  {
    "path": "SpringApp/Base.lproj/Main.storyboard",
    "chars": 60308,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
  },
  {
    "path": "SpringApp/CodeViewController.swift",
    "chars": 2570,
    "preview": "//\n//  CodeViewController.swift\n//  DesignerNewsApp\n//\n//  Created by Meng To on 2015-01-05.\n//  Copyright (c) 2015 Meng"
  },
  {
    "path": "SpringApp/Images.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 1655,
    "preview": "{\n  \"images\" : [\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"appicon@58.png\",\n      \"sca"
  },
  {
    "path": "SpringApp/Images.xcassets/icon-shape.imageset/Contents.json",
    "chars": 159,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icon-shape.pdf\"\n    }\n  ],\n  \"info\" : {\n    \"ver"
  },
  {
    "path": "SpringApp/Images.xcassets/loading.imageset/Contents.json",
    "chars": 156,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"loading.pdf\"\n    }\n  ],\n  \"info\" : {\n    \"versio"
  },
  {
    "path": "SpringApp/Info.plist",
    "chars": 1259,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "SpringApp/OptionsViewController.swift",
    "chars": 4894,
    "preview": "//\n//  OptionsViewController.swift\n//  DesignerNewsApp\n//\n//  Created by Meng To on 2015-01-04.\n//  Copyright (c) 2015 M"
  },
  {
    "path": "SpringApp/SpringViewController.swift",
    "chars": 8459,
    "preview": "//\n//  SpringViewController.swift\n//  DesignerNewsApp\n//\n//  Created by Meng To on 2015-01-02.\n//  Copyright (c) 2015 Me"
  },
  {
    "path": "SpringApp.xcodeproj/project.pbxproj",
    "chars": 43163,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "SpringApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 154,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:SpringApp.xcode"
  },
  {
    "path": "SpringApp.xcodeproj/xcshareddata/xcschemes/Spring.xcscheme",
    "chars": 2850,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"0900\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "SpringApp.xcodeproj/xcshareddata/xcschemes/SpringApp.xcscheme",
    "chars": 5369,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"0900\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "SpringAppTests/Info.plist",
    "chars": 733,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "SpringAppTests/SpringAppTests.swift",
    "chars": 896,
    "preview": "//\n//  SpringAppTests.swift\n//  SpringAppTests\n//\n//  Created by Meng To on 2015-01-06.\n//  Copyright (c) 2015 Meng To. "
  },
  {
    "path": "SpringTests/Info.plist",
    "chars": 733,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "SpringTests/SpringTests.swift",
    "chars": 887,
    "preview": "//\n//  SpringTests.swift\n//  SpringTests\n//\n//  Created by James Tang on 20/1/15.\n//  Copyright (c) 2015 Meng To. All ri"
  }
]

About this extraction

This page contains the full source code of the MengTo/Spring GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 53 files (242.6 KB), approximately 63.2k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!