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.

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

## 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 = "shake"" 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
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.