├── .gitignore ├── .swift-version ├── LICENSE ├── README.md ├── Spring.podspec ├── 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 ├── SpringApp.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── SpringApp.xccheckout └── xcshareddata │ └── xcschemes │ ├── Spring.xcscheme │ └── SpringApp.xcscheme ├── SpringApp ├── AppDelegate.swift ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard ├── CodeViewController.swift ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── appicon@120-1.png │ │ ├── appicon@120.png │ │ ├── appicon@152.png │ │ ├── appicon@167.png │ │ ├── appicon@180.png │ │ ├── appicon@29.png │ │ ├── appicon@40.png │ │ ├── appicon@58-1.png │ │ ├── appicon@58.png │ │ ├── appicon@76.png │ │ ├── appicon@80-1.png │ │ ├── appicon@80.png │ │ └── appicon@87.png │ ├── icon-shape.imageset │ │ ├── Contents.json │ │ └── icon-shape.pdf │ └── loading.imageset │ │ ├── Contents.json │ │ └── loading.pdf ├── Info.plist ├── OptionsViewController.swift └── SpringViewController.swift ├── SpringAppTests ├── Info.plist └── SpringAppTests.swift └── SpringTests ├── Info.plist └── SpringTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.pbxuser 3 | !default.pbxuser 4 | *.mode1v3 5 | !default.mode1v3 6 | *.mode2v3 7 | !default.mode2v3 8 | *.perspectivev3 9 | !default.perspectivev3 10 | xcuserdata 11 | *.xccheckout 12 | *.moved-aside 13 | DerivedData 14 | *.hmap 15 | *.ipa 16 | *.xcuserstate 17 | .DS_Store 18 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2015 Meng To (meng@designcode.io) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Updated for Swift 4.2 2 | Requires Xcode 10 and Swift 4.2. 3 | 4 | ## Installation 5 | Drop in the Spring folder to your Xcode project (make sure to enable "Copy items if needed" and "Create groups"). 6 | 7 | Or via CocoaPods: 8 | ``` 9 | use_frameworks! 10 | pod 'Spring', :git => 'https://github.com/MengTo/Spring.git' 11 | ``` 12 | 13 | ## Usage with Storyboard 14 | In Identity Inspector, connect the UIView to SpringView Class and set the animation properties in Attribute Inspector. 15 | 16 | ![](http://cl.ly/image/241o0G1G3S36/download/springsetup.jpg) 17 | 18 | ## Usage with Code 19 | layer.animation = "squeezeDown" 20 | layer.animate() 21 | 22 | ## Demo The Animations 23 | ![](http://cl.ly/image/1n1E2j3W3y24/springscreen.jpg) 24 | 25 | ## Chaining Animations 26 | layer.y = -50 27 | animateToNext { 28 | layer.animation = "fall" 29 | layer.animateTo() 30 | } 31 | 32 | ## Functions 33 | animate() 34 | animateNext { ... } 35 | animateTo() 36 | animateToNext { ... } 37 | 38 | ## Animation 39 | shake 40 | pop 41 | morph 42 | squeeze 43 | wobble 44 | swing 45 | flipX 46 | flipY 47 | fall 48 | squeezeLeft 49 | squeezeRight 50 | squeezeDown 51 | squeezeUp 52 | slideLeft 53 | slideRight 54 | slideDown 55 | slideUp 56 | fadeIn 57 | fadeOut 58 | fadeInLeft 59 | fadeInRight 60 | fadeInDown 61 | fadeInUp 62 | zoomIn 63 | zoomOut 64 | flash 65 | 66 | ## Curve 67 | spring 68 | linear 69 | easeIn 70 | easeOut 71 | easeInOut 72 | 73 | ## Properties 74 | force 75 | duration 76 | delay 77 | damping 78 | velocity 79 | repeatCount 80 | scale 81 | x 82 | y 83 | rotate 84 | 85 | \* Not all properties work together. Play with the demo app. 86 | 87 | 88 | ## Autostart 89 | Allows you to animate without code. Don't need to use this if you plan to start the animation in code. 90 | 91 | ## Autohide 92 | Saves you the hassle of adding a line "layer.alpha = 0" in viewDidLoad(). 93 | 94 | ## Known issue 95 | Animations won't autostart when view is reached via performSegueWithIdentifier. 96 | 97 | ## Tutorials 98 | - Tutorials available on [Design+Code](https://designcode.io/swiftapp). 99 | - [Integrate Spring to existing Objective-C projects](https://medium.com/ios-apprentice/using-swift-in-objective-c-projects-f7e7a09f8be) 100 | 101 | ## ChangeLog 102 | - At [ChangeLog](https://github.com/MengTo/Spring/wiki/CHANGELOG) wiki page 103 | 104 | ## License 105 | 106 | Spring is released under the MIT license. See LICENSE for details. 107 | -------------------------------------------------------------------------------- /Spring.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'Spring' 3 | s.version = '1.0.6' 4 | s.license = 'MIT' 5 | s.summary = 'A library to simplify iOS animations in Swift.' 6 | s.homepage = 'https://github.com/MengTo/Spring' 7 | s.authors = { 'Meng To' => 'meng@designcode.io' } 8 | s.source = { :git => 'https://github.com/MengTo/Spring.git', :tag => s.version.to_s } 9 | s.requires_arc = true 10 | s.ios.deployment_target = '8.0' 11 | s.tvos.deployment_target = '11.0' 12 | s.source_files = 'Spring/*.swift' 13 | s.ios.resources = ['Spring/*.xib', 'SpringApp/*.xcassets'] 14 | s.tvos.resources = ['SpringApp/*.xcassets'] 15 | end 16 | -------------------------------------------------------------------------------- /Spring/AsyncButton.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 James Tang (j@jamztang.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | public class AsyncButton: UIButton { 26 | 27 | private var imageURL = [UInt:NSURL]() 28 | private var placeholderImage = [UInt:UIImage]() 29 | 30 | 31 | public func setImageURL(url: NSURL?, placeholderImage placeholder:UIImage?, forState state:UIControl.State) { 32 | 33 | imageURL[state.rawValue] = url 34 | placeholderImage[state.rawValue] = placeholder 35 | 36 | if let urlString = url?.absoluteString { 37 | ImageLoader.sharedLoader.imageForUrl(urlString: urlString) { [weak self] image, url in 38 | 39 | if let strongSelf = self { 40 | 41 | DispatchQueue.main.async(execute: { () -> Void in 42 | if strongSelf.imageURL[state.rawValue]?.absoluteString == url { 43 | strongSelf.setImage(image, for: state) 44 | } 45 | }) 46 | } 47 | } 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Spring/AsyncImageView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 James Tang (j@jamztang.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | public class AsyncImageView: UIImageView { 26 | 27 | public var placeholderImage : UIImage? 28 | 29 | public var url : NSURL? { 30 | didSet { 31 | self.image = placeholderImage 32 | if let urlString = url?.absoluteString { 33 | ImageLoader.sharedLoader.imageForUrl(urlString: urlString) { [weak self] image, url in 34 | if let strongSelf = self { 35 | DispatchQueue.main.async(execute: { () -> Void in 36 | if strongSelf.url?.absoluteString == url { 37 | strongSelf.image = image ?? strongSelf.placeholderImage 38 | } 39 | }) 40 | } 41 | } 42 | } 43 | } 44 | } 45 | 46 | public func setURL(url: NSURL?, placeholderImage: UIImage?) { 47 | self.placeholderImage = placeholderImage 48 | self.url = url 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Spring/AutoTextView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AutoTextView.swift 3 | // SpringApp 4 | // 5 | // Created by Meng To on 2015-03-27. 6 | // Copyright (c) 2015 Meng To. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class AutoTextView: UITextView { 12 | 13 | public override var intrinsicContentSize: CGSize { 14 | get { 15 | var size = self.sizeThatFits(CGSize(width: self.frame.size.width, height: CGFloat.greatestFiniteMagnitude)) 16 | size.width = self.frame.size.width 17 | if text.length == 0 { 18 | size.height = 0 19 | } 20 | 21 | contentInset = UIEdgeInsets(top: -4, left: -4, bottom: -4, right: -4) 22 | layoutIfNeeded() 23 | 24 | return size 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Spring/BlurView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | public func insertBlurView (view: UIView, style: UIBlurEffect.Style) -> UIVisualEffectView { 26 | view.backgroundColor = UIColor.clear 27 | 28 | let blurEffect = UIBlurEffect(style: style) 29 | let blurEffectView = UIVisualEffectView(effect: blurEffect) 30 | blurEffectView.frame = view.bounds 31 | view.insertSubview(blurEffectView, at: 0) 32 | return blurEffectView 33 | } 34 | -------------------------------------------------------------------------------- /Spring/DesignableButton.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | @IBDesignable public class DesignableButton: SpringButton { 26 | 27 | @IBInspectable public var borderColor: UIColor = UIColor.clear { 28 | didSet { 29 | layer.borderColor = borderColor.cgColor 30 | } 31 | } 32 | 33 | @IBInspectable public var borderWidth: CGFloat = 0 { 34 | didSet { 35 | layer.borderWidth = borderWidth 36 | } 37 | } 38 | 39 | @IBInspectable public var cornerRadius: CGFloat = 0 { 40 | didSet { 41 | layer.cornerRadius = cornerRadius 42 | } 43 | } 44 | 45 | @IBInspectable public var shadowColor: UIColor = UIColor.clear { 46 | didSet { 47 | layer.shadowColor = shadowColor.cgColor 48 | } 49 | } 50 | 51 | @IBInspectable public var shadowRadius: CGFloat = 0 { 52 | didSet { 53 | layer.shadowRadius = shadowRadius 54 | } 55 | } 56 | 57 | @IBInspectable public var shadowOpacity: CGFloat = 0 { 58 | didSet { 59 | layer.shadowOpacity = Float(shadowOpacity) 60 | } 61 | } 62 | 63 | @IBInspectable public var shadowOffsetY: CGFloat = 0 { 64 | didSet { 65 | layer.shadowOffset.height = shadowOffsetY 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /Spring/DesignableImageView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | @IBDesignable public class DesignableImageView: SpringImageView { 26 | 27 | @IBInspectable public var borderColor: UIColor = UIColor.clear { 28 | didSet { 29 | layer.borderColor = borderColor.cgColor 30 | } 31 | } 32 | 33 | @IBInspectable public var borderWidth: CGFloat = 0 { 34 | didSet { 35 | layer.borderWidth = borderWidth 36 | } 37 | } 38 | 39 | @IBInspectable public var cornerRadius: CGFloat = 0 { 40 | didSet { 41 | layer.cornerRadius = cornerRadius 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /Spring/DesignableLabel.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | @IBDesignable public class DesignableLabel: SpringLabel { 26 | 27 | @IBInspectable public var lineHeight: CGFloat = 1.5 { 28 | didSet { 29 | let font = UIFont(name: self.font.fontName, size: self.font.pointSize) 30 | guard let text = self.text else { return } 31 | 32 | let paragraphStyle = NSMutableParagraphStyle() 33 | paragraphStyle.lineSpacing = lineHeight 34 | 35 | let attributedString = NSMutableAttributedString(string: text) 36 | attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, attributedString.length)) 37 | attributedString.addAttribute(NSAttributedString.Key.font, value: font!, range: NSMakeRange(0, attributedString.length)) 38 | 39 | self.attributedText = attributedString 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Spring/DesignableTabBarController.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | @IBDesignable class DesignableTabBarController: UITabBarController { 26 | 27 | @IBInspectable var normalTint: UIColor = UIColor.clear { 28 | didSet { 29 | UITabBar.appearance().tintColor = normalTint 30 | UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: normalTint], for: UIControl.State()) 31 | } 32 | } 33 | 34 | @IBInspectable var selectedTint: UIColor = UIColor.clear { 35 | didSet { 36 | UITabBar.appearance().tintColor = selectedTint 37 | UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: selectedTint], for:UIControl.State.selected) 38 | } 39 | } 40 | 41 | @IBInspectable var fontName: String = "" { 42 | didSet { 43 | UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: normalTint, NSAttributedString.Key.font: UIFont(name: fontName, size: 11)!], for: UIControl.State()) 44 | } 45 | } 46 | 47 | @IBInspectable var firstSelectedImage: UIImage? { 48 | didSet { 49 | if let image = firstSelectedImage { 50 | var tabBarItems = self.tabBar.items as [UITabBarItem]? 51 | tabBarItems?[0].selectedImage = image.withRenderingMode(UIImage.RenderingMode.alwaysTemplate) 52 | } 53 | } 54 | } 55 | 56 | @IBInspectable var secondSelectedImage: UIImage? { 57 | didSet { 58 | if let image = secondSelectedImage { 59 | var tabBarItems = self.tabBar.items as [UITabBarItem]? 60 | tabBarItems?[1].selectedImage = image.withRenderingMode(UIImage.RenderingMode.alwaysTemplate) 61 | } 62 | } 63 | } 64 | 65 | @IBInspectable var thirdSelectedImage: UIImage? { 66 | didSet { 67 | if let image = thirdSelectedImage { 68 | var tabBarItems = self.tabBar.items as [UITabBarItem]? 69 | tabBarItems?[2].selectedImage = image.withRenderingMode(UIImage.RenderingMode.alwaysTemplate) 70 | } 71 | } 72 | } 73 | 74 | @IBInspectable var fourthSelectedImage: UIImage? { 75 | didSet { 76 | if let image = fourthSelectedImage { 77 | var tabBarItems = self.tabBar.items as [UITabBarItem]? 78 | tabBarItems?[3].selectedImage = image.withRenderingMode(UIImage.RenderingMode.alwaysTemplate) 79 | } 80 | } 81 | } 82 | 83 | @IBInspectable var fifthSelectedImage: UIImage? { 84 | didSet { 85 | if let image = fifthSelectedImage { 86 | var tabBarItems = self.tabBar.items as [UITabBarItem]? 87 | tabBarItems?[4].selectedImage = image.withRenderingMode(UIImage.RenderingMode.alwaysTemplate) 88 | } 89 | } 90 | } 91 | 92 | override func viewDidLoad() { 93 | super.viewDidLoad() 94 | if let items = self.tabBar.items { 95 | for item in items { 96 | if let image = item.image { 97 | item.image = image.imageWithColor(tintColor: self.normalTint).withRenderingMode(UIImage.RenderingMode.alwaysOriginal) 98 | } 99 | } 100 | } 101 | } 102 | } 103 | 104 | extension UIImage { 105 | func imageWithColor(tintColor: UIColor) -> UIImage { 106 | UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale) 107 | 108 | let context = UIGraphicsGetCurrentContext() 109 | context!.translateBy(x: 0, y: self.size.height) 110 | context!.scaleBy(x: 1.0, y: -1.0); 111 | context!.setBlendMode(CGBlendMode.normal) 112 | 113 | let rect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height) 114 | context?.clip(to: rect, mask: self.cgImage!) 115 | tintColor.setFill() 116 | context!.fill(rect) 117 | 118 | let newImage = UIGraphicsGetImageFromCurrentImageContext()! as UIImage 119 | UIGraphicsEndImageContext() 120 | 121 | return newImage 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /Spring/DesignableTextField.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | @IBDesignable public class DesignableTextField: SpringTextField { 26 | 27 | @IBInspectable public var placeholderColor: UIColor = UIColor.clear { 28 | didSet { 29 | guard let placeholder = placeholder else { return } 30 | attributedPlaceholder = NSAttributedString(string: placeholder, attributes: [NSAttributedString.Key.foregroundColor: placeholderColor]) 31 | layoutSubviews() 32 | 33 | } 34 | } 35 | 36 | @IBInspectable public var sidePadding: CGFloat = 0 { 37 | didSet { 38 | let padding = UIView(frame: CGRect(x: 0, y: 0, width: sidePadding, height: sidePadding)) 39 | 40 | leftViewMode = UITextField.ViewMode.always 41 | leftView = padding 42 | 43 | rightViewMode = UITextField.ViewMode.always 44 | rightView = padding 45 | } 46 | } 47 | 48 | @IBInspectable public var leftPadding: CGFloat = 0 { 49 | didSet { 50 | let padding = UIView(frame: CGRect(x: 0, y: 0, width: leftPadding, height: 0)) 51 | 52 | leftViewMode = UITextField.ViewMode.always 53 | leftView = padding 54 | } 55 | } 56 | 57 | @IBInspectable public var rightPadding: CGFloat = 0 { 58 | didSet { 59 | let padding = UIView(frame: CGRect(x: 0, y: 0, width: rightPadding, height: 0)) 60 | 61 | rightViewMode = UITextField.ViewMode.always 62 | rightView = padding 63 | } 64 | } 65 | 66 | @IBInspectable public var borderColor: UIColor = UIColor.clear { 67 | didSet { 68 | layer.borderColor = borderColor.cgColor 69 | } 70 | } 71 | 72 | @IBInspectable public var borderWidth: CGFloat = 0 { 73 | didSet { 74 | layer.borderWidth = borderWidth 75 | } 76 | } 77 | 78 | @IBInspectable public var cornerRadius: CGFloat = 0 { 79 | didSet { 80 | layer.cornerRadius = cornerRadius 81 | } 82 | } 83 | 84 | @IBInspectable public var lineHeight: CGFloat = 1.5 { 85 | didSet { 86 | let font = UIFont(name: self.font!.fontName, size: self.font!.pointSize) 87 | guard let text = self.text else { return } 88 | 89 | let paragraphStyle = NSMutableParagraphStyle() 90 | paragraphStyle.lineSpacing = lineHeight 91 | 92 | let attributedString = NSMutableAttributedString(string: text) 93 | attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length)) 94 | attributedString.addAttribute(NSAttributedString.Key.font, value: font!, range: NSRange(location: 0, length: attributedString.length)) 95 | 96 | self.attributedText = attributedString 97 | } 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /Spring/DesignableTextView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | @IBDesignable public class DesignableTextView: SpringTextView { 26 | 27 | @IBInspectable public var borderColor: UIColor = UIColor.clear { 28 | didSet { 29 | layer.borderColor = borderColor.cgColor 30 | } 31 | } 32 | 33 | @IBInspectable public var borderWidth: CGFloat = 0 { 34 | didSet { 35 | layer.borderWidth = borderWidth 36 | } 37 | } 38 | 39 | @IBInspectable public var cornerRadius: CGFloat = 0 { 40 | didSet { 41 | layer.cornerRadius = cornerRadius 42 | } 43 | } 44 | 45 | @IBInspectable public var lineHeight: CGFloat = 1.5 { 46 | didSet { 47 | let font = UIFont(name: self.font!.fontName, size: self.font!.pointSize) 48 | guard let text = self.text else { return } 49 | 50 | let paragraphStyle = NSMutableParagraphStyle() 51 | paragraphStyle.lineSpacing = lineHeight 52 | 53 | let attributedString = NSMutableAttributedString(string: text) 54 | attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length)) 55 | attributedString.addAttribute(NSAttributedString.Key.font, value: font!, range: NSRange(location: 0, length: attributedString.length)) 56 | 57 | self.attributedText = attributedString 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /Spring/DesignableView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | @IBDesignable public class DesignableView: SpringView { 26 | 27 | @IBInspectable public var borderColor: UIColor = UIColor.clear { 28 | didSet { 29 | layer.borderColor = borderColor.cgColor 30 | } 31 | } 32 | 33 | @IBInspectable public var borderWidth: CGFloat = 0 { 34 | didSet { 35 | layer.borderWidth = borderWidth 36 | } 37 | } 38 | 39 | @IBInspectable public var cornerRadius: CGFloat = 0 { 40 | didSet { 41 | layer.cornerRadius = cornerRadius 42 | } 43 | } 44 | 45 | @IBInspectable public var shadowColor: UIColor = UIColor.clear { 46 | didSet { 47 | layer.shadowColor = shadowColor.cgColor 48 | } 49 | } 50 | 51 | @IBInspectable public var shadowRadius: CGFloat = 0 { 52 | didSet { 53 | layer.shadowRadius = shadowRadius 54 | } 55 | } 56 | 57 | @IBInspectable public var shadowOpacity: CGFloat = 0 { 58 | didSet { 59 | layer.shadowOpacity = Float(shadowOpacity) 60 | } 61 | } 62 | 63 | @IBInspectable public var shadowOffsetY: CGFloat = 0 { 64 | didSet { 65 | layer.shadowOffset.height = shadowOffsetY 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Spring/ImageLoader.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Nate Lyman (https://github.com/natelyman/SwiftImageLoader) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | import Foundation 25 | 26 | 27 | public class ImageLoader { 28 | 29 | var cache = NSCache() 30 | 31 | public class var sharedLoader : ImageLoader { 32 | struct Static { 33 | static let instance : ImageLoader = ImageLoader() 34 | } 35 | return Static.instance 36 | } 37 | 38 | public func imageForUrl(urlString: String, completionHandler: @escaping(_ image: UIImage?, _ url: String) -> ()) { 39 | DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async { 40 | var data: NSData? 41 | 42 | if let dataCache = self.cache.object(forKey: urlString as NSString){ 43 | data = (dataCache) as NSData 44 | 45 | }else{ 46 | if (URL(string: urlString) != nil) 47 | { 48 | data = NSData(contentsOf: URL(string: urlString)!) 49 | if data != nil { 50 | self.cache.setObject(data!, forKey: urlString as NSString) 51 | } 52 | }else{ 53 | return 54 | } 55 | } 56 | 57 | if let goodData = data { 58 | let image = UIImage(data: goodData as Data) 59 | DispatchQueue.main.async(execute: {() in 60 | completionHandler(image, urlString) 61 | }) 62 | return 63 | } 64 | 65 | let downloadTask: URLSessionDataTask = URLSession.shared.dataTask(with: URL(string: urlString)!, completionHandler: { (data, response, error) -> Void in 66 | 67 | if (error != nil) { 68 | completionHandler(nil, urlString) 69 | return 70 | } 71 | 72 | if data != nil { 73 | let image = UIImage(data: data!) 74 | self.cache.setObject(data! as NSData, forKey: urlString as NSString) 75 | DispatchQueue.main.async(execute: {() in 76 | completionHandler(image, urlString) 77 | }) 78 | return 79 | } 80 | }) 81 | downloadTask.resume() 82 | 83 | } 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Spring/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Spring/KeyboardLayoutConstraint.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 James Tang (j@jamztang.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | #if !os(tvOS) 26 | @available(tvOS, unavailable) 27 | public class KeyboardLayoutConstraint: NSLayoutConstraint { 28 | 29 | private var offset : CGFloat = 0 30 | private var keyboardVisibleHeight : CGFloat = 0 31 | 32 | @available(tvOS, unavailable) 33 | override public func awakeFromNib() { 34 | super.awakeFromNib() 35 | 36 | offset = constant 37 | 38 | NotificationCenter.default.addObserver(self, selector: #selector(KeyboardLayoutConstraint.keyboardWillShowNotification(_:)), name: UIWindow.keyboardWillShowNotification, object: nil) 39 | NotificationCenter.default.addObserver(self, selector: #selector(KeyboardLayoutConstraint.keyboardWillHideNotification(_:)), name: UIWindow.keyboardWillHideNotification, object: nil) 40 | } 41 | 42 | deinit { 43 | NotificationCenter.default.removeObserver(self) 44 | } 45 | 46 | // MARK: Notification 47 | 48 | @objc func keyboardWillShowNotification(_ notification: Notification) { 49 | if let userInfo = notification.userInfo { 50 | if let frameValue = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue { 51 | let frame = frameValue.cgRectValue 52 | keyboardVisibleHeight = frame.size.height 53 | } 54 | 55 | self.updateConstant() 56 | switch (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber, userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber) { 57 | case let (.some(duration), .some(curve)): 58 | 59 | let options = UIView.AnimationOptions(rawValue: curve.uintValue) 60 | 61 | UIView.animate( 62 | withDuration: TimeInterval(duration.doubleValue), 63 | delay: 0, 64 | options: options, 65 | animations: { 66 | UIApplication.shared.keyWindow?.layoutIfNeeded() 67 | return 68 | }, completion: { finished in 69 | }) 70 | default: 71 | 72 | break 73 | } 74 | 75 | } 76 | 77 | } 78 | 79 | @objc func keyboardWillHideNotification(_ notification: NSNotification) { 80 | keyboardVisibleHeight = 0 81 | self.updateConstant() 82 | 83 | if let userInfo = notification.userInfo { 84 | 85 | switch (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber, userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber) { 86 | case let (.some(duration), .some(curve)): 87 | 88 | let options = UIView.AnimationOptions(rawValue: curve.uintValue) 89 | 90 | UIView.animate( 91 | withDuration: TimeInterval(duration.doubleValue), 92 | delay: 0, 93 | options: options, 94 | animations: { 95 | UIApplication.shared.keyWindow?.layoutIfNeeded() 96 | return 97 | }, completion: { finished in 98 | }) 99 | default: 100 | break 101 | } 102 | } 103 | } 104 | 105 | func updateConstant() { 106 | self.constant = offset + keyboardVisibleHeight 107 | } 108 | 109 | } 110 | #endif 111 | -------------------------------------------------------------------------------- /Spring/LoadingView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | #if !os(tvOS) 26 | @available(tvOS, unavailable) 27 | public class LoadingView: UIView { 28 | 29 | @IBOutlet public weak var indicatorView: SpringView! 30 | 31 | override public func awakeFromNib() { 32 | let animation = CABasicAnimation() 33 | animation.keyPath = "transform.rotation.z" 34 | animation.fromValue = degreesToRadians(degrees: 0) 35 | animation.toValue = degreesToRadians(degrees: 360) 36 | animation.duration = 0.9 37 | animation.repeatCount = HUGE 38 | indicatorView.layer.add(animation, forKey: "") 39 | } 40 | 41 | class func designCodeLoadingView() -> UIView { 42 | 43 | return Bundle(for: self).loadNibNamed("LoadingView", owner: self, options: nil)![0] as! UIView 44 | } 45 | } 46 | 47 | public extension UIView { 48 | 49 | struct LoadingViewConstants { 50 | static let Tag = 1000 51 | } 52 | 53 | public func showLoading() { 54 | 55 | if self.viewWithTag(LoadingViewConstants.Tag) != nil { 56 | // If loading view is already found in current view hierachy, do nothing 57 | return 58 | } 59 | 60 | let loadingXibView = LoadingView.designCodeLoadingView() 61 | loadingXibView.frame = self.bounds 62 | loadingXibView.tag = LoadingViewConstants.Tag 63 | self.addSubview(loadingXibView) 64 | 65 | loadingXibView.alpha = 0 66 | SpringAnimation.spring(duration: 0.7, animations: { 67 | loadingXibView.alpha = 1 68 | }) 69 | } 70 | 71 | public func hideLoading() { 72 | 73 | if let loadingXibView = self.viewWithTag(LoadingViewConstants.Tag) { 74 | loadingXibView.alpha = 1 75 | 76 | SpringAnimation.springWithCompletion(duration: 0.7, animations: { 77 | loadingXibView.alpha = 0 78 | loadingXibView.transform = CGAffineTransform(scaleX: 3, y: 3) 79 | }, completion: { (completed) -> Void in 80 | loadingXibView.removeFromSuperview() 81 | }) 82 | } 83 | } 84 | 85 | } 86 | #endif 87 | -------------------------------------------------------------------------------- /Spring/LoadingView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Spring/Misc.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | public extension String { 26 | var length: Int { return self.count } 27 | 28 | func toURL() -> NSURL? { 29 | return NSURL(string: self) 30 | } 31 | } 32 | 33 | public func htmlToAttributedString(text: String) -> NSAttributedString! { 34 | guard let htmlData = text.data(using: String.Encoding.utf8, allowLossyConversion: false) else { 35 | return NSAttributedString() } 36 | let htmlString: NSAttributedString? 37 | do { 38 | htmlString = try NSAttributedString(data: htmlData, options: [NSAttributedString.DocumentReadingOptionKey.documentType:NSAttributedString.DocumentType.html], documentAttributes: nil) 39 | } catch _ { 40 | htmlString = nil 41 | } 42 | 43 | return htmlString 44 | } 45 | 46 | public func degreesToRadians(degrees: CGFloat) -> CGFloat { 47 | return degrees * CGFloat(CGFloat.pi / 180) 48 | } 49 | 50 | public func delay(delay:Double, closure: @escaping ()->()) { 51 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure) 52 | } 53 | 54 | public func imageFromURL(_ Url: String) -> UIImage { 55 | let url = Foundation.URL(string: Url) 56 | let data = try? Data(contentsOf: url!) 57 | return UIImage(data: data!)! 58 | } 59 | 60 | public extension UIColor { 61 | convenience init(hex: String) { 62 | var red: CGFloat = 0.0 63 | var green: CGFloat = 0.0 64 | var blue: CGFloat = 0.0 65 | var alpha: CGFloat = 1.0 66 | var hex: String = hex 67 | 68 | if hex.hasPrefix("#") { 69 | let index = hex.index(hex.startIndex, offsetBy: 1) 70 | hex = String(hex[index...]) 71 | } 72 | 73 | let scanner = Scanner(string: hex) 74 | var hexValue: CUnsignedLongLong = 0 75 | if scanner.scanHexInt64(&hexValue) { 76 | switch (hex.count) { 77 | case 3: 78 | red = CGFloat((hexValue & 0xF00) >> 8) / 15.0 79 | green = CGFloat((hexValue & 0x0F0) >> 4) / 15.0 80 | blue = CGFloat(hexValue & 0x00F) / 15.0 81 | case 4: 82 | red = CGFloat((hexValue & 0xF000) >> 12) / 15.0 83 | green = CGFloat((hexValue & 0x0F00) >> 8) / 15.0 84 | blue = CGFloat((hexValue & 0x00F0) >> 4) / 15.0 85 | alpha = CGFloat(hexValue & 0x000F) / 15.0 86 | case 6: 87 | red = CGFloat((hexValue & 0xFF0000) >> 16) / 255.0 88 | green = CGFloat((hexValue & 0x00FF00) >> 8) / 255.0 89 | blue = CGFloat(hexValue & 0x0000FF) / 255.0 90 | case 8: 91 | red = CGFloat((hexValue & 0xFF000000) >> 24) / 255.0 92 | green = CGFloat((hexValue & 0x00FF0000) >> 16) / 255.0 93 | blue = CGFloat((hexValue & 0x0000FF00) >> 8) / 255.0 94 | alpha = CGFloat(hexValue & 0x000000FF) / 255.0 95 | default: 96 | print("Invalid RGB string, number of characters after '#' should be either 3, 4, 6 or 8", terminator: "") 97 | } 98 | } else { 99 | print("Scan hex error") 100 | } 101 | self.init(red:red, green:green, blue:blue, alpha:alpha) 102 | } 103 | } 104 | 105 | public func rgbaToUIColor(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) -> UIColor { 106 | 107 | return UIColor(red: red, green: green, blue: blue, alpha: alpha) 108 | } 109 | 110 | public func UIColorFromRGB(rgbValue: UInt) -> UIColor { 111 | return UIColor( 112 | red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, 113 | green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, 114 | blue: CGFloat(rgbValue & 0x0000FF) / 255.0, 115 | alpha: CGFloat(1.0) 116 | ) 117 | } 118 | 119 | public func stringFromDate(date: NSDate, format: String) -> String { 120 | let dateFormatter = DateFormatter() 121 | dateFormatter.dateFormat = format 122 | return dateFormatter.string(from: date as Date) 123 | } 124 | 125 | public func dateFromString(date: String, format: String) -> Date { 126 | let dateFormatter = DateFormatter() 127 | dateFormatter.dateFormat = format 128 | if let date = dateFormatter.date(from: date) { 129 | return date 130 | } else { 131 | return Date(timeIntervalSince1970: 0) 132 | } 133 | } 134 | 135 | public func randomStringWithLength (len : Int) -> NSString { 136 | 137 | let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 138 | 139 | let randomString : NSMutableString = NSMutableString(capacity: len) 140 | 141 | for _ in 0 ..< len { 142 | let length = UInt32 (letters.length) 143 | let rand = arc4random_uniform(length) 144 | randomString.appendFormat("%C", letters.character(at: Int(rand))) 145 | } 146 | 147 | return randomString 148 | } 149 | 150 | public func timeAgoSinceDate(date: Date, numericDates: Bool) -> String { 151 | let calendar = Calendar.current 152 | let unitFlags = Set(arrayLiteral: Calendar.Component.minute, Calendar.Component.hour, Calendar.Component.day, Calendar.Component.weekOfYear, Calendar.Component.month, Calendar.Component.year, Calendar.Component.second) 153 | let now = Date() 154 | let dateComparison = now.compare(date) 155 | var earliest: Date 156 | var latest: Date 157 | 158 | switch dateComparison { 159 | case .orderedAscending: 160 | earliest = now 161 | latest = date 162 | default: 163 | earliest = date 164 | latest = now 165 | } 166 | 167 | let components: DateComponents = calendar.dateComponents(unitFlags, from: earliest, to: latest) 168 | 169 | guard 170 | let year = components.year, 171 | let month = components.month, 172 | let weekOfYear = components.weekOfYear, 173 | let day = components.day, 174 | let hour = components.hour, 175 | let minute = components.minute, 176 | let second = components.second 177 | else { 178 | fatalError() 179 | } 180 | 181 | if (year >= 2) { 182 | return "\(year)y" 183 | } else if (year >= 1) { 184 | if (numericDates){ 185 | return "1y" 186 | } else { 187 | return "1y" 188 | } 189 | } else if (month >= 2) { 190 | return "\(month * 4)w" 191 | } else if (month >= 1) { 192 | if (numericDates){ 193 | return "4w" 194 | } else { 195 | return "4w" 196 | } 197 | } else if (weekOfYear >= 2) { 198 | return "\(weekOfYear)w" 199 | } else if (weekOfYear >= 1){ 200 | if (numericDates){ 201 | return "1w" 202 | } else { 203 | return "1w" 204 | } 205 | } else if (day >= 2) { 206 | return "\(components.day ?? 2)d" 207 | } else if (day >= 1){ 208 | if (numericDates){ 209 | return "1d" 210 | } else { 211 | return "1d" 212 | } 213 | } else if (hour >= 2) { 214 | return "\(hour)h" 215 | } else if (hour >= 1){ 216 | if (numericDates){ 217 | return "1h" 218 | } else { 219 | return "1h" 220 | } 221 | } else if (minute >= 2) { 222 | return "\(minute)m" 223 | } else if (minute >= 1){ 224 | if (numericDates){ 225 | return "1m" 226 | } else { 227 | return "1m" 228 | } 229 | } else if (second >= 3) { 230 | return "\(second)s" 231 | } else { 232 | return "now" 233 | } 234 | 235 | } 236 | 237 | extension UIImageView { 238 | func setImage(url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit, placeholderImage: UIImage?) { 239 | contentMode = mode 240 | URLSession.shared.dataTask(with: url) { (data, response, error) in 241 | guard 242 | let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200, 243 | let mimeType = response?.mimeType, mimeType.hasPrefix("image"), 244 | let data = data, error == nil, 245 | let image = UIImage(data: data) 246 | else { 247 | self.image = placeholderImage 248 | return 249 | } 250 | DispatchQueue.main.async() { () -> Void in 251 | self.image = image 252 | 253 | } 254 | }.resume() 255 | } 256 | func setImage(urlString: String, contentMode mode: UIView.ContentMode = .scaleAspectFit, placeholderImage: UIImage?) { 257 | guard let url = URL(string: urlString) else { 258 | image = placeholderImage 259 | return 260 | } 261 | setImage(url: url, contentMode: mode, placeholderImage: placeholderImage) 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /Spring/SoundPlayer.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 James Tang (j@jamztang.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | import AudioToolbox 25 | 26 | public struct SoundPlayer { 27 | 28 | static var filename : String? 29 | static var enabled : Bool = true 30 | 31 | private struct Internal { 32 | static var cache = [URL:SystemSoundID]() 33 | } 34 | 35 | public static func playSound(soundFile: String) { 36 | 37 | if !enabled { 38 | return 39 | } 40 | 41 | if let url = Bundle.main.url(forResource: soundFile, withExtension: nil) { 42 | 43 | var soundID : SystemSoundID = Internal.cache[url] ?? 0 44 | 45 | if soundID == 0 { 46 | AudioServicesCreateSystemSoundID(url as CFURL, &soundID) 47 | Internal.cache[url] = soundID 48 | } 49 | 50 | AudioServicesPlaySystemSound(soundID) 51 | 52 | } else { 53 | print("Could not find sound file name `\(soundFile)`") 54 | } 55 | } 56 | 57 | static func play(file: String) { 58 | self.playSound(soundFile: file) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Spring/Spring.h: -------------------------------------------------------------------------------- 1 | // 2 | // Spring.h 3 | // Spring 4 | // 5 | // Created by James Tang on 20/1/15. 6 | // Copyright (c) 2015 Meng To. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Spring. 12 | FOUNDATION_EXPORT double SpringVersionNumber; 13 | 14 | //! Project version string for Spring. 15 | FOUNDATION_EXPORT const unsigned char SpringVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Spring/Spring.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | @objc public protocol Springable { 26 | var autostart: Bool { get set } 27 | var autohide: Bool { get set } 28 | var animation: String { get set } 29 | var force: CGFloat { get set } 30 | var delay: CGFloat { get set } 31 | var duration: CGFloat { get set } 32 | var damping: CGFloat { get set } 33 | var velocity: CGFloat { get set } 34 | var repeatCount: Float { get set } 35 | var x: CGFloat { get set } 36 | var y: CGFloat { get set } 37 | var scaleX: CGFloat { get set } 38 | var scaleY: CGFloat { get set } 39 | var rotate: CGFloat { get set } 40 | var opacity: CGFloat { get set } 41 | var animateFrom: Bool { get set } 42 | var curve: String { get set } 43 | 44 | // UIView 45 | var layer : CALayer { get } 46 | var transform : CGAffineTransform { get set } 47 | var alpha : CGFloat { get set } 48 | 49 | func animate() 50 | func animateNext(completion: @escaping () -> ()) 51 | func animateTo() 52 | func animateToNext(completion: @escaping () -> ()) 53 | } 54 | 55 | public class Spring : NSObject { 56 | 57 | private unowned var view : Springable 58 | private var shouldAnimateAfterActive = false 59 | private var shouldAnimateInLayoutSubviews = true 60 | 61 | init(_ view: Springable) { 62 | self.view = view 63 | super.init() 64 | commonInit() 65 | } 66 | 67 | func commonInit() { 68 | NotificationCenter.default.addObserver(self, selector: #selector(Spring.didBecomeActiveNotification(_:)), name: UIApplication.didBecomeActiveNotification, object: nil) 69 | } 70 | 71 | @objc func didBecomeActiveNotification(_ notification: NSNotification) { 72 | if shouldAnimateAfterActive { 73 | alpha = 0 74 | animate() 75 | shouldAnimateAfterActive = false 76 | } 77 | } 78 | 79 | deinit { 80 | NotificationCenter.default.removeObserver(self, name: UIApplication.didBecomeActiveNotification, object: nil) 81 | } 82 | 83 | private var autostart: Bool { set { self.view.autostart = newValue } get { return self.view.autostart }} 84 | private var autohide: Bool { set { self.view.autohide = newValue } get { return self.view.autohide }} 85 | private var animation: String { set { self.view.animation = newValue } get { return self.view.animation }} 86 | private var force: CGFloat { set { self.view.force = newValue } get { return self.view.force }} 87 | private var delay: CGFloat { set { self.view.delay = newValue } get { return self.view.delay }} 88 | private var duration: CGFloat { set { self.view.duration = newValue } get { return self.view.duration }} 89 | private var damping: CGFloat { set { self.view.damping = newValue } get { return self.view.damping }} 90 | private var velocity: CGFloat { set { self.view.velocity = newValue } get { return self.view.velocity }} 91 | private var repeatCount: Float { set { self.view.repeatCount = newValue } get { return self.view.repeatCount }} 92 | private var x: CGFloat { set { self.view.x = newValue } get { return self.view.x }} 93 | private var y: CGFloat { set { self.view.y = newValue } get { return self.view.y }} 94 | private var scaleX: CGFloat { set { self.view.scaleX = newValue } get { return self.view.scaleX }} 95 | private var scaleY: CGFloat { set { self.view.scaleY = newValue } get { return self.view.scaleY }} 96 | private var rotate: CGFloat { set { self.view.rotate = newValue } get { return self.view.rotate }} 97 | private var opacity: CGFloat { set { self.view.opacity = newValue } get { return self.view.opacity }} 98 | private var animateFrom: Bool { set { self.view.animateFrom = newValue } get { return self.view.animateFrom }} 99 | private var curve: String { set { self.view.curve = newValue } get { return self.view.curve }} 100 | 101 | // UIView 102 | private var layer : CALayer { return view.layer } 103 | private var transform : CGAffineTransform { get { return view.transform } set { view.transform = newValue }} 104 | private var alpha: CGFloat { get { return view.alpha } set { view.alpha = newValue } } 105 | 106 | public enum AnimationPreset: String { 107 | case SlideLeft = "slideLeft" 108 | case SlideRight = "slideRight" 109 | case SlideDown = "slideDown" 110 | case SlideUp = "slideUp" 111 | case SqueezeLeft = "squeezeLeft" 112 | case SqueezeRight = "squeezeRight" 113 | case SqueezeDown = "squeezeDown" 114 | case SqueezeUp = "squeezeUp" 115 | case FadeIn = "fadeIn" 116 | case FadeOut = "fadeOut" 117 | case FadeOutIn = "fadeOutIn" 118 | case FadeInLeft = "fadeInLeft" 119 | case FadeInRight = "fadeInRight" 120 | case FadeInDown = "fadeInDown" 121 | case FadeInUp = "fadeInUp" 122 | case ZoomIn = "zoomIn" 123 | case ZoomOut = "zoomOut" 124 | case Fall = "fall" 125 | case Shake = "shake" 126 | case Pop = "pop" 127 | case FlipX = "flipX" 128 | case FlipY = "flipY" 129 | case Morph = "morph" 130 | case Squeeze = "squeeze" 131 | case Flash = "flash" 132 | case Wobble = "wobble" 133 | case Swing = "swing" 134 | } 135 | 136 | public enum AnimationCurve: String { 137 | case EaseIn = "easeIn" 138 | case EaseOut = "easeOut" 139 | case EaseInOut = "easeInOut" 140 | case Linear = "linear" 141 | case Spring = "spring" 142 | case EaseInSine = "easeInSine" 143 | case EaseOutSine = "easeOutSine" 144 | case EaseInOutSine = "easeInOutSine" 145 | case EaseInQuad = "easeInQuad" 146 | case EaseOutQuad = "easeOutQuad" 147 | case EaseInOutQuad = "easeInOutQuad" 148 | case EaseInCubic = "easeInCubic" 149 | case EaseOutCubic = "easeOutCubic" 150 | case EaseInOutCubic = "easeInOutCubic" 151 | case EaseInQuart = "easeInQuart" 152 | case EaseOutQuart = "easeOutQuart" 153 | case EaseInOutQuart = "easeInOutQuart" 154 | case EaseInQuint = "easeInQuint" 155 | case EaseOutQuint = "easeOutQuint" 156 | case EaseInOutQuint = "easeInOutQuint" 157 | case EaseInExpo = "easeInExpo" 158 | case EaseOutExpo = "easeOutExpo" 159 | case EaseInOutExpo = "easeInOutExpo" 160 | case EaseInCirc = "easeInCirc" 161 | case EaseOutCirc = "easeOutCirc" 162 | case EaseInOutCirc = "easeInOutCirc" 163 | case EaseInBack = "easeInBack" 164 | case EaseOutBack = "easeOutBack" 165 | case EaseInOutBack = "easeInOutBack" 166 | } 167 | 168 | func animatePreset() { 169 | alpha = 0.99 170 | if let animation = AnimationPreset(rawValue: animation) { 171 | switch animation { 172 | case .SlideLeft: 173 | x = 300*force 174 | case .SlideRight: 175 | x = -300*force 176 | case .SlideDown: 177 | y = -300*force 178 | case .SlideUp: 179 | y = 300*force 180 | case .SqueezeLeft: 181 | x = 300 182 | scaleX = 3*force 183 | case .SqueezeRight: 184 | x = -300 185 | scaleX = 3*force 186 | case .SqueezeDown: 187 | y = -300 188 | scaleY = 3*force 189 | case .SqueezeUp: 190 | y = 300 191 | scaleY = 3*force 192 | case .FadeIn: 193 | opacity = 0 194 | case .FadeOut: 195 | animateFrom = false 196 | opacity = 0 197 | case .FadeOutIn: 198 | let animation = CABasicAnimation() 199 | animation.keyPath = "opacity" 200 | animation.fromValue = 1 201 | animation.toValue = 0 202 | animation.timingFunction = getTimingFunction(curve: curve) 203 | animation.duration = CFTimeInterval(duration) 204 | animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) 205 | animation.autoreverses = true 206 | layer.add(animation, forKey: "fade") 207 | case .FadeInLeft: 208 | opacity = 0 209 | x = 300*force 210 | case .FadeInRight: 211 | x = -300*force 212 | opacity = 0 213 | case .FadeInDown: 214 | y = -300*force 215 | opacity = 0 216 | case .FadeInUp: 217 | y = 300*force 218 | opacity = 0 219 | case .ZoomIn: 220 | opacity = 0 221 | scaleX = 2*force 222 | scaleY = 2*force 223 | case .ZoomOut: 224 | animateFrom = false 225 | opacity = 0 226 | scaleX = 2*force 227 | scaleY = 2*force 228 | case .Fall: 229 | animateFrom = false 230 | rotate = 15 * CGFloat(CGFloat.pi/180) 231 | y = 600*force 232 | case .Shake: 233 | let animation = CAKeyframeAnimation() 234 | animation.keyPath = "position.x" 235 | animation.values = [0, 30*force, -30*force, 30*force, 0] 236 | animation.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] 237 | animation.timingFunction = getTimingFunction(curve: curve) 238 | animation.duration = CFTimeInterval(duration) 239 | animation.isAdditive = true 240 | animation.repeatCount = repeatCount 241 | animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) 242 | layer.add(animation, forKey: "shake") 243 | case .Pop: 244 | let animation = CAKeyframeAnimation() 245 | animation.keyPath = "transform.scale" 246 | animation.values = [0, 0.2*force, -0.2*force, 0.2*force, 0] 247 | animation.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] 248 | animation.timingFunction = getTimingFunction(curve: curve) 249 | animation.duration = CFTimeInterval(duration) 250 | animation.isAdditive = true 251 | animation.repeatCount = repeatCount 252 | animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) 253 | layer.add(animation, forKey: "pop") 254 | case .FlipX: 255 | rotate = 0 256 | scaleX = 1 257 | scaleY = 1 258 | var perspective = CATransform3DIdentity 259 | perspective.m34 = -1.0 / layer.frame.size.width/2 260 | 261 | let animation = CABasicAnimation() 262 | animation.keyPath = "transform" 263 | animation.fromValue = NSValue(caTransform3D: CATransform3DMakeRotation(0, 0, 0, 0)) 264 | animation.toValue = NSValue(caTransform3D: 265 | CATransform3DConcat(perspective, CATransform3DMakeRotation(CGFloat(CGFloat.pi), 0, 1, 0))) 266 | animation.duration = CFTimeInterval(duration) 267 | animation.repeatCount = repeatCount 268 | animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) 269 | animation.timingFunction = getTimingFunction(curve: curve) 270 | layer.add(animation, forKey: "3d") 271 | case .FlipY: 272 | var perspective = CATransform3DIdentity 273 | perspective.m34 = -1.0 / layer.frame.size.width/2 274 | 275 | let animation = CABasicAnimation() 276 | animation.keyPath = "transform" 277 | animation.fromValue = NSValue(caTransform3D: 278 | CATransform3DMakeRotation(0, 0, 0, 0)) 279 | animation.toValue = NSValue(caTransform3D: 280 | CATransform3DConcat(perspective,CATransform3DMakeRotation(CGFloat(CGFloat.pi), 1, 0, 0))) 281 | animation.duration = CFTimeInterval(duration) 282 | animation.repeatCount = repeatCount 283 | animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) 284 | animation.timingFunction = getTimingFunction(curve: curve) 285 | layer.add(animation, forKey: "3d") 286 | case .Morph: 287 | let morphX = CAKeyframeAnimation() 288 | morphX.keyPath = "transform.scale.x" 289 | morphX.values = [1, 1.3*force, 0.7, 1.3*force, 1] 290 | morphX.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] 291 | morphX.timingFunction = getTimingFunction(curve: curve) 292 | morphX.duration = CFTimeInterval(duration) 293 | morphX.repeatCount = repeatCount 294 | morphX.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) 295 | layer.add(morphX, forKey: "morphX") 296 | 297 | let morphY = CAKeyframeAnimation() 298 | morphY.keyPath = "transform.scale.y" 299 | morphY.values = [1, 0.7, 1.3*force, 0.7, 1] 300 | morphY.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] 301 | morphY.timingFunction = getTimingFunction(curve: curve) 302 | morphY.duration = CFTimeInterval(duration) 303 | morphY.repeatCount = repeatCount 304 | morphY.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) 305 | layer.add(morphY, forKey: "morphY") 306 | case .Squeeze: 307 | let morphX = CAKeyframeAnimation() 308 | morphX.keyPath = "transform.scale.x" 309 | morphX.values = [1, 1.5*force, 0.5, 1.5*force, 1] 310 | morphX.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] 311 | morphX.timingFunction = getTimingFunction(curve: curve) 312 | morphX.duration = CFTimeInterval(duration) 313 | morphX.repeatCount = repeatCount 314 | morphX.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) 315 | layer.add(morphX, forKey: "morphX") 316 | 317 | let morphY = CAKeyframeAnimation() 318 | morphY.keyPath = "transform.scale.y" 319 | morphY.values = [1, 0.5, 1, 0.5, 1] 320 | morphY.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] 321 | morphY.timingFunction = getTimingFunction(curve: curve) 322 | morphY.duration = CFTimeInterval(duration) 323 | morphY.repeatCount = repeatCount 324 | morphY.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) 325 | layer.add(morphY, forKey: "morphY") 326 | case .Flash: 327 | let animation = CABasicAnimation() 328 | animation.keyPath = "opacity" 329 | animation.fromValue = 1 330 | animation.toValue = 0 331 | animation.duration = CFTimeInterval(duration) 332 | animation.repeatCount = repeatCount * 2.0 333 | animation.autoreverses = true 334 | animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) 335 | layer.add(animation, forKey: "flash") 336 | case .Wobble: 337 | let animation = CAKeyframeAnimation() 338 | animation.keyPath = "transform.rotation" 339 | animation.values = [0, 0.3*force, -0.3*force, 0.3*force, 0] 340 | animation.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] 341 | animation.duration = CFTimeInterval(duration) 342 | animation.isAdditive = true 343 | animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) 344 | layer.add(animation, forKey: "wobble") 345 | 346 | let x = CAKeyframeAnimation() 347 | x.keyPath = "position.x" 348 | x.values = [0, 30*force, -30*force, 30*force, 0] 349 | x.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] 350 | x.timingFunction = getTimingFunction(curve: curve) 351 | x.duration = CFTimeInterval(duration) 352 | x.isAdditive = true 353 | x.repeatCount = repeatCount 354 | x.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) 355 | layer.add(x, forKey: "x") 356 | case .Swing: 357 | let animation = CAKeyframeAnimation() 358 | animation.keyPath = "transform.rotation" 359 | animation.values = [0, 0.3*force, -0.3*force, 0.3*force, 0] 360 | animation.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] 361 | animation.duration = CFTimeInterval(duration) 362 | animation.isAdditive = true 363 | animation.repeatCount = repeatCount 364 | animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) 365 | layer.add(animation, forKey: "swing") 366 | } 367 | } 368 | } 369 | 370 | func getTimingFunction(curve: String) -> CAMediaTimingFunction { 371 | if let curve = AnimationCurve(rawValue: curve) { 372 | switch curve { 373 | case .EaseIn: return CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn) 374 | case .EaseOut: return CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) 375 | case .EaseInOut: return CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) 376 | case .Linear: return CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) 377 | case .Spring: return CAMediaTimingFunction(controlPoints: 0.5, 1.1+Float(force/3), 1, 1) 378 | case .EaseInSine: return CAMediaTimingFunction(controlPoints: 0.47, 0, 0.745, 0.715) 379 | case .EaseOutSine: return CAMediaTimingFunction(controlPoints: 0.39, 0.575, 0.565, 1) 380 | case .EaseInOutSine: return CAMediaTimingFunction(controlPoints: 0.445, 0.05, 0.55, 0.95) 381 | case .EaseInQuad: return CAMediaTimingFunction(controlPoints: 0.55, 0.085, 0.68, 0.53) 382 | case .EaseOutQuad: return CAMediaTimingFunction(controlPoints: 0.25, 0.46, 0.45, 0.94) 383 | case .EaseInOutQuad: return CAMediaTimingFunction(controlPoints: 0.455, 0.03, 0.515, 0.955) 384 | case .EaseInCubic: return CAMediaTimingFunction(controlPoints: 0.55, 0.055, 0.675, 0.19) 385 | case .EaseOutCubic: return CAMediaTimingFunction(controlPoints: 0.215, 0.61, 0.355, 1) 386 | case .EaseInOutCubic: return CAMediaTimingFunction(controlPoints: 0.645, 0.045, 0.355, 1) 387 | case .EaseInQuart: return CAMediaTimingFunction(controlPoints: 0.895, 0.03, 0.685, 0.22) 388 | case .EaseOutQuart: return CAMediaTimingFunction(controlPoints: 0.165, 0.84, 0.44, 1) 389 | case .EaseInOutQuart: return CAMediaTimingFunction(controlPoints: 0.77, 0, 0.175, 1) 390 | case .EaseInQuint: return CAMediaTimingFunction(controlPoints: 0.755, 0.05, 0.855, 0.06) 391 | case .EaseOutQuint: return CAMediaTimingFunction(controlPoints: 0.23, 1, 0.32, 1) 392 | case .EaseInOutQuint: return CAMediaTimingFunction(controlPoints: 0.86, 0, 0.07, 1) 393 | case .EaseInExpo: return CAMediaTimingFunction(controlPoints: 0.95, 0.05, 0.795, 0.035) 394 | case .EaseOutExpo: return CAMediaTimingFunction(controlPoints: 0.19, 1, 0.22, 1) 395 | case .EaseInOutExpo: return CAMediaTimingFunction(controlPoints: 1, 0, 0, 1) 396 | case .EaseInCirc: return CAMediaTimingFunction(controlPoints: 0.6, 0.04, 0.98, 0.335) 397 | case .EaseOutCirc: return CAMediaTimingFunction(controlPoints: 0.075, 0.82, 0.165, 1) 398 | case .EaseInOutCirc: return CAMediaTimingFunction(controlPoints: 0.785, 0.135, 0.15, 0.86) 399 | case .EaseInBack: return CAMediaTimingFunction(controlPoints: 0.6, -0.28, 0.735, 0.045) 400 | case .EaseOutBack: return CAMediaTimingFunction(controlPoints: 0.175, 0.885, 0.32, 1.275) 401 | case .EaseInOutBack: return CAMediaTimingFunction(controlPoints: 0.68, -0.55, 0.265, 1.55) 402 | } 403 | } 404 | return CAMediaTimingFunction(name: CAMediaTimingFunctionName.default) 405 | } 406 | 407 | func getAnimationOptions(curve: String) -> UIView.AnimationOptions { 408 | if let curve = AnimationCurve(rawValue: curve) { 409 | switch curve { 410 | case .EaseIn: return UIView.AnimationOptions.curveEaseIn 411 | case .EaseOut: return UIView.AnimationOptions.curveEaseOut 412 | case .EaseInOut: return UIView.AnimationOptions() 413 | default: break 414 | } 415 | } 416 | return UIView.AnimationOptions.curveLinear 417 | } 418 | 419 | public func animate() { 420 | animateFrom = true 421 | animatePreset() 422 | setView {} 423 | } 424 | 425 | public func animateNext(completion: @escaping () -> ()) { 426 | animateFrom = true 427 | animatePreset() 428 | setView { 429 | completion() 430 | } 431 | } 432 | 433 | public func animateTo() { 434 | animateFrom = false 435 | animatePreset() 436 | setView {} 437 | } 438 | 439 | public func animateToNext(completion: @escaping () -> ()) { 440 | animateFrom = false 441 | animatePreset() 442 | setView { 443 | completion() 444 | } 445 | } 446 | 447 | public func customAwakeFromNib() { 448 | if autohide { 449 | alpha = 0 450 | } 451 | } 452 | 453 | public func customLayoutSubviews() { 454 | if shouldAnimateInLayoutSubviews { 455 | shouldAnimateInLayoutSubviews = false 456 | if autostart { 457 | if UIApplication.shared.applicationState != .active { 458 | shouldAnimateAfterActive = true 459 | return 460 | } 461 | alpha = 0 462 | animate() 463 | } 464 | } 465 | } 466 | 467 | func setView(completion: @escaping () -> ()) { 468 | if animateFrom { 469 | let translate = CGAffineTransform(translationX: self.x, y: self.y) 470 | let scale = CGAffineTransform(scaleX: self.scaleX, y: self.scaleY) 471 | let rotate = CGAffineTransform(rotationAngle: self.rotate) 472 | let translateAndScale = translate.concatenating(scale) 473 | self.transform = rotate.concatenating(translateAndScale) 474 | 475 | self.alpha = self.opacity 476 | } 477 | 478 | UIView.animate( withDuration: TimeInterval(duration), 479 | delay: TimeInterval(delay), 480 | usingSpringWithDamping: damping, 481 | initialSpringVelocity: velocity, 482 | options: [getAnimationOptions(curve: curve), UIView.AnimationOptions.allowUserInteraction], 483 | animations: { [weak self] in 484 | if let _self = self 485 | { 486 | if _self.animateFrom { 487 | _self.transform = CGAffineTransform.identity 488 | _self.alpha = 1 489 | } 490 | else { 491 | let translate = CGAffineTransform(translationX: _self.x, y: _self.y) 492 | let scale = CGAffineTransform(scaleX: _self.scaleX, y: _self.scaleY) 493 | let rotate = CGAffineTransform(rotationAngle: _self.rotate) 494 | let translateAndScale = translate.concatenating(scale) 495 | _self.transform = rotate.concatenating(translateAndScale) 496 | 497 | _self.alpha = _self.opacity 498 | } 499 | 500 | } 501 | 502 | }, completion: { [weak self] finished in 503 | 504 | completion() 505 | self?.resetAll() 506 | 507 | }) 508 | 509 | } 510 | 511 | func reset() { 512 | x = 0 513 | y = 0 514 | opacity = 1 515 | } 516 | 517 | func resetAll() { 518 | x = 0 519 | y = 0 520 | animation = "" 521 | opacity = 1 522 | scaleX = 1 523 | scaleY = 1 524 | rotate = 0 525 | damping = 0.7 526 | velocity = 0.7 527 | repeatCount = 1 528 | delay = 0 529 | duration = 0.7 530 | } 531 | 532 | } 533 | -------------------------------------------------------------------------------- /Spring/SpringAnimation.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | @objc public class SpringAnimation: NSObject { 26 | public class func spring(duration: TimeInterval, animations: @escaping () -> Void) { 27 | UIView.animate( 28 | withDuration: duration, 29 | delay: 0, 30 | usingSpringWithDamping: 0.7, 31 | initialSpringVelocity: 0.7, 32 | options: [], 33 | animations: { 34 | animations() 35 | }, 36 | completion: nil 37 | ) 38 | } 39 | 40 | public class func springEaseIn(duration: TimeInterval, animations: (() -> Void)!) { 41 | UIView.animate( 42 | withDuration: duration, 43 | delay: 0, 44 | options: .curveEaseIn, 45 | animations: { 46 | animations() 47 | }, 48 | completion: nil 49 | ) 50 | } 51 | 52 | public class func springEaseOut(duration: TimeInterval, animations: (() -> Void)!) { 53 | UIView.animate( 54 | withDuration: duration, 55 | delay: 0, 56 | options: .curveEaseOut, 57 | animations: { 58 | animations() 59 | }, completion: nil 60 | ) 61 | } 62 | 63 | public class func springEaseInOut(duration: TimeInterval, animations: (() -> Void)!) { 64 | UIView.animate( 65 | withDuration: duration, 66 | delay: 0, 67 | options: UIView.AnimationOptions(), 68 | animations: { 69 | animations() 70 | }, completion: nil 71 | ) 72 | } 73 | 74 | public class func springLinear(duration: TimeInterval, animations: (() -> Void)!) { 75 | UIView.animate( 76 | withDuration: duration, 77 | delay: 0, 78 | options: .curveLinear, 79 | animations: { 80 | animations() 81 | }, completion: nil 82 | ) 83 | } 84 | 85 | public class func springWithDelay(duration: TimeInterval, delay: TimeInterval, animations: (() -> Void)!) { 86 | UIView.animate( 87 | withDuration: duration, 88 | delay: delay, 89 | usingSpringWithDamping: 0.7, 90 | initialSpringVelocity: 0.7, 91 | options: [], 92 | animations: { 93 | animations() 94 | }, completion: nil 95 | ) 96 | } 97 | 98 | public class func springWithCompletion(duration: TimeInterval, animations: (() -> Void)!, completion: ((Bool) -> Void)!) { 99 | UIView.animate( 100 | withDuration: duration, 101 | delay: 0, 102 | usingSpringWithDamping: 0.7, 103 | initialSpringVelocity: 0.7, 104 | options: [], 105 | animations: { 106 | animations() 107 | }, completion: { finished in 108 | completion(finished) 109 | } 110 | ) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Spring/SpringButton.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | open class SpringButton: UIButton, Springable { 26 | @IBInspectable public var autostart: Bool = false 27 | @IBInspectable public var autohide: Bool = false 28 | @IBInspectable public var animation: String = "" 29 | @IBInspectable public var force: CGFloat = 1 30 | @IBInspectable public var delay: CGFloat = 0 31 | @IBInspectable public var duration: CGFloat = 0.7 32 | @IBInspectable public var damping: CGFloat = 0.7 33 | @IBInspectable public var velocity: CGFloat = 0.7 34 | @IBInspectable public var repeatCount: Float = 1 35 | @IBInspectable public var x: CGFloat = 0 36 | @IBInspectable public var y: CGFloat = 0 37 | @IBInspectable public var scaleX: CGFloat = 1 38 | @IBInspectable public var scaleY: CGFloat = 1 39 | @IBInspectable public var rotate: CGFloat = 0 40 | @IBInspectable public var curve: String = "" 41 | public var opacity: CGFloat = 1 42 | public var animateFrom: Bool = false 43 | 44 | lazy private var spring : Spring = Spring(self) 45 | 46 | override open func awakeFromNib() { 47 | super.awakeFromNib() 48 | self.spring.customAwakeFromNib() 49 | } 50 | 51 | open override func layoutSubviews() { 52 | super.layoutSubviews() 53 | spring.customLayoutSubviews() 54 | } 55 | 56 | public func animate() { 57 | self.spring.animate() 58 | } 59 | 60 | public func animateNext(completion: @escaping () -> ()) { 61 | self.spring.animateNext(completion: completion) 62 | } 63 | 64 | public func animateTo() { 65 | self.spring.animateTo() 66 | } 67 | 68 | public func animateToNext(completion: @escaping () -> ()) { 69 | self.spring.animateToNext(completion: completion) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Spring/SpringImageView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | open class SpringImageView: UIImageView, Springable { 26 | @IBInspectable public var autostart: Bool = false 27 | @IBInspectable public var autohide: Bool = false 28 | @IBInspectable public var animation: String = "" 29 | @IBInspectable public var force: CGFloat = 1 30 | @IBInspectable public var delay: CGFloat = 0 31 | @IBInspectable public var duration: CGFloat = 0.7 32 | @IBInspectable public var damping: CGFloat = 0.7 33 | @IBInspectable public var velocity: CGFloat = 0.7 34 | @IBInspectable public var repeatCount: Float = 1 35 | @IBInspectable public var x: CGFloat = 0 36 | @IBInspectable public var y: CGFloat = 0 37 | @IBInspectable public var scaleX: CGFloat = 1 38 | @IBInspectable public var scaleY: CGFloat = 1 39 | @IBInspectable public var rotate: CGFloat = 0 40 | @IBInspectable public var curve: String = "" 41 | public var opacity: CGFloat = 1 42 | public var animateFrom: Bool = false 43 | 44 | lazy private var spring : Spring = Spring(self) 45 | 46 | override open func awakeFromNib() { 47 | super.awakeFromNib() 48 | self.spring.customAwakeFromNib() 49 | } 50 | 51 | open override func layoutSubviews() { 52 | super.layoutSubviews() 53 | spring.customLayoutSubviews() 54 | } 55 | 56 | public func animate() { 57 | self.spring.animate() 58 | } 59 | 60 | public func animateNext(completion: @escaping () -> ()) { 61 | self.spring.animateNext(completion: completion) 62 | } 63 | 64 | public func animateTo() { 65 | self.spring.animateTo() 66 | } 67 | 68 | public func animateToNext(completion: @escaping () -> ()) { 69 | self.spring.animateToNext(completion: completion) 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /Spring/SpringLabel.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | open class SpringLabel: UILabel, Springable { 26 | @IBInspectable public var autostart: Bool = false 27 | @IBInspectable public var autohide: Bool = false 28 | @IBInspectable public var animation: String = "" 29 | @IBInspectable public var force: CGFloat = 1 30 | @IBInspectable public var delay: CGFloat = 0 31 | @IBInspectable public var duration: CGFloat = 0.7 32 | @IBInspectable public var damping: CGFloat = 0.7 33 | @IBInspectable public var velocity: CGFloat = 0.7 34 | @IBInspectable public var repeatCount: Float = 1 35 | @IBInspectable public var x: CGFloat = 0 36 | @IBInspectable public var y: CGFloat = 0 37 | @IBInspectable public var scaleX: CGFloat = 1 38 | @IBInspectable public var scaleY: CGFloat = 1 39 | @IBInspectable public var rotate: CGFloat = 0 40 | @IBInspectable public var curve: String = "" 41 | public var opacity: CGFloat = 1 42 | public var animateFrom: Bool = false 43 | 44 | lazy private var spring : Spring = Spring(self) 45 | 46 | override open func awakeFromNib() { 47 | super.awakeFromNib() 48 | self.spring.customAwakeFromNib() 49 | } 50 | 51 | open override func layoutSubviews() { 52 | super.layoutSubviews() 53 | spring.customLayoutSubviews() 54 | } 55 | 56 | public func animate() { 57 | self.spring.animate() 58 | } 59 | 60 | public func animateNext(completion: @escaping () -> ()) { 61 | self.spring.animateNext(completion: completion) 62 | } 63 | 64 | public func animateTo() { 65 | self.spring.animateTo() 66 | } 67 | 68 | public func animateToNext(completion: @escaping () -> ()) { 69 | self.spring.animateToNext(completion: completion) 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /Spring/SpringTextField.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | open class SpringTextField: UITextField, Springable { 26 | @IBInspectable public var autostart: Bool = false 27 | @IBInspectable public var autohide: Bool = false 28 | @IBInspectable public var animation: String = "" 29 | @IBInspectable public var force: CGFloat = 1 30 | @IBInspectable public var delay: CGFloat = 0 31 | @IBInspectable public var duration: CGFloat = 0.7 32 | @IBInspectable public var damping: CGFloat = 0.7 33 | @IBInspectable public var velocity: CGFloat = 0.7 34 | @IBInspectable public var repeatCount: Float = 1 35 | @IBInspectable public var x: CGFloat = 0 36 | @IBInspectable public var y: CGFloat = 0 37 | @IBInspectable public var scaleX: CGFloat = 1 38 | @IBInspectable public var scaleY: CGFloat = 1 39 | @IBInspectable public var rotate: CGFloat = 0 40 | @IBInspectable public var curve: String = "" 41 | public var opacity: CGFloat = 1 42 | public var animateFrom: Bool = false 43 | 44 | lazy private var spring : Spring = Spring(self) 45 | 46 | override open func awakeFromNib() { 47 | super.awakeFromNib() 48 | self.spring.customAwakeFromNib() 49 | } 50 | 51 | open override func layoutSubviews() { 52 | super.layoutSubviews() 53 | spring.customLayoutSubviews() 54 | } 55 | 56 | public func animate() { 57 | self.spring.animate() 58 | } 59 | 60 | public func animateNext(completion: @escaping () -> ()) { 61 | self.spring.animateNext(completion: completion) 62 | } 63 | 64 | public func animateTo() { 65 | self.spring.animateTo() 66 | } 67 | 68 | public func animateToNext(completion: @escaping () -> ()) { 69 | self.spring.animateToNext(completion: completion) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Spring/SpringTextView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | open class SpringTextView: UITextView, Springable { 26 | @IBInspectable public var autostart: Bool = false 27 | @IBInspectable public var autohide: Bool = false 28 | @IBInspectable public var animation: String = "" 29 | @IBInspectable public var force: CGFloat = 1 30 | @IBInspectable public var delay: CGFloat = 0 31 | @IBInspectable public var duration: CGFloat = 0.7 32 | @IBInspectable public var damping: CGFloat = 0.7 33 | @IBInspectable public var velocity: CGFloat = 0.7 34 | @IBInspectable public var repeatCount: Float = 1 35 | @IBInspectable public var x: CGFloat = 0 36 | @IBInspectable public var y: CGFloat = 0 37 | @IBInspectable public var scaleX: CGFloat = 1 38 | @IBInspectable public var scaleY: CGFloat = 1 39 | @IBInspectable public var rotate: CGFloat = 0 40 | @IBInspectable public var curve: String = "" 41 | public var opacity: CGFloat = 1 42 | public var animateFrom: Bool = false 43 | 44 | lazy private var spring : Spring = Spring(self) 45 | 46 | override open func awakeFromNib() { 47 | super.awakeFromNib() 48 | self.spring.customAwakeFromNib() 49 | } 50 | 51 | open override func layoutSubviews() { 52 | super.layoutSubviews() 53 | spring.customLayoutSubviews() 54 | } 55 | 56 | public func animate() { 57 | self.spring.animate() 58 | } 59 | 60 | public func animateNext(completion: @escaping () -> ()) { 61 | self.spring.animateNext(completion: completion) 62 | } 63 | 64 | public func animateTo() { 65 | self.spring.animateTo() 66 | } 67 | 68 | public func animateToNext(completion: @escaping () -> ()) { 69 | self.spring.animateToNext(completion: completion) 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /Spring/SpringView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | open class SpringView: UIView, Springable { 26 | @IBInspectable public var autostart: Bool = false 27 | @IBInspectable public var autohide: Bool = false 28 | @IBInspectable public var animation: String = "" 29 | @IBInspectable public var force: CGFloat = 1 30 | @IBInspectable public var delay: CGFloat = 0 31 | @IBInspectable public var duration: CGFloat = 0.7 32 | @IBInspectable public var damping: CGFloat = 0.7 33 | @IBInspectable public var velocity: CGFloat = 0.7 34 | @IBInspectable public var repeatCount: Float = 1 35 | @IBInspectable public var x: CGFloat = 0 36 | @IBInspectable public var y: CGFloat = 0 37 | @IBInspectable public var scaleX: CGFloat = 1 38 | @IBInspectable public var scaleY: CGFloat = 1 39 | @IBInspectable public var rotate: CGFloat = 0 40 | @IBInspectable public var curve: String = "" 41 | public var opacity: CGFloat = 1 42 | public var animateFrom: Bool = false 43 | 44 | lazy private var spring : Spring = Spring(self) 45 | 46 | override open func awakeFromNib() { 47 | super.awakeFromNib() 48 | self.spring.customAwakeFromNib() 49 | } 50 | 51 | open override func layoutSubviews() { 52 | super.layoutSubviews() 53 | spring.customLayoutSubviews() 54 | } 55 | 56 | public func animate() { 57 | self.spring.animate() 58 | } 59 | 60 | public func animateNext(completion: @escaping () -> ()) { 61 | self.spring.animateNext(completion: completion) 62 | } 63 | 64 | public func animateTo() { 65 | self.spring.animateTo() 66 | } 67 | 68 | public func animateToNext(completion: @escaping () -> ()) { 69 | self.spring.animateToNext(completion: completion) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Spring/TransitionManager.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | public class TransitionManager: NSObject, UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning { 26 | 27 | var isPresenting = true 28 | var duration = 0.3 29 | 30 | public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 31 | let container = transitionContext.containerView 32 | let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)! 33 | let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)! 34 | 35 | if isPresenting { 36 | toView.frame = container.bounds 37 | toView.transform = CGAffineTransform(translationX: 0, y: container.frame.size.height) 38 | container.addSubview(fromView) 39 | container.addSubview(toView) 40 | SpringAnimation.springEaseInOut(duration: duration) { 41 | fromView.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) 42 | fromView.alpha = 0.5 43 | toView.transform = CGAffineTransform.identity 44 | } 45 | } 46 | else { 47 | 48 | // 1. Rotating will change the bounds 49 | // 2. we have to properly reset toView 50 | // to the actual container's bounds, at 51 | // the same time take consideration of 52 | // previous transformation when presenting 53 | let transform = toView.transform 54 | toView.transform = CGAffineTransform.identity 55 | toView.frame = container.bounds 56 | toView.transform = transform 57 | 58 | container.addSubview(toView) 59 | container.addSubview(fromView) 60 | 61 | SpringAnimation.springEaseInOut(duration: duration) { 62 | fromView.transform = CGAffineTransform(translationX: 0, y: fromView.frame.size.height) 63 | toView.transform = CGAffineTransform.identity 64 | toView.alpha = 1 65 | } 66 | } 67 | 68 | delay(delay: duration, closure: { 69 | transitionContext.completeTransition(true) 70 | }) 71 | } 72 | 73 | public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 74 | return duration 75 | } 76 | 77 | public func animationController(forPresentedController presented: UIViewController, presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 78 | isPresenting = true 79 | return self 80 | } 81 | 82 | public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 83 | isPresenting = false 84 | return self 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Spring/TransitionZoom.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | public class TransitionZoom: NSObject, UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning { 26 | 27 | var isPresenting = true 28 | var duration = 0.4 29 | 30 | public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 31 | let container = transitionContext.containerView 32 | let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)! 33 | let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)! 34 | 35 | if isPresenting { 36 | container.addSubview(fromView) 37 | container.addSubview(toView) 38 | 39 | toView.alpha = 0 40 | toView.transform = CGAffineTransform(scaleX: 2, y: 2) 41 | 42 | SpringAnimation.springEaseInOut(duration: duration) { 43 | fromView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5) 44 | fromView.alpha = 0 45 | toView.transform = CGAffineTransform.identity 46 | toView.alpha = 1 47 | } 48 | } 49 | else { 50 | container.addSubview(toView) 51 | container.addSubview(fromView) 52 | 53 | SpringAnimation.springEaseInOut(duration: duration) { 54 | fromView.transform = CGAffineTransform(scaleX: 2, y: 2) 55 | fromView.alpha = 0 56 | toView.transform = CGAffineTransform(scaleX: 1, y: 1) 57 | toView.alpha = 1 58 | } 59 | } 60 | 61 | delay(delay: duration, closure: { 62 | transitionContext.completeTransition(true) 63 | }) 64 | } 65 | 66 | public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 67 | return duration 68 | } 69 | 70 | public func animationController(forPresentedController presented: UIViewController, presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 71 | isPresenting = true 72 | return self 73 | } 74 | 75 | public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 76 | isPresenting = false 77 | return self 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Spring/UnwindSegue.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Meng To (meng@designcode.io) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import UIKit 24 | 25 | public extension UIViewController { 26 | @IBAction public func unwindToViewController (_ segue: UIStoryboardSegue){} 27 | } 28 | -------------------------------------------------------------------------------- /SpringApp.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1A4FDA381A6E44780099D309 /* Spring.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A4FDA371A6E44780099D309 /* Spring.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 1A4FDA3E1A6E44780099D309 /* Spring.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A4FDA331A6E44780099D309 /* Spring.framework */; }; 12 | 1A4FDA471A6E44780099D309 /* SpringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A4FDA461A6E44780099D309 /* SpringTests.swift */; }; 13 | 1A4FDA4A1A6E44780099D309 /* Spring.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A4FDA331A6E44780099D309 /* Spring.framework */; }; 14 | 1A4FDA4B1A6E44780099D309 /* Spring.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 1A4FDA331A6E44780099D309 /* Spring.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 15 | 1A4FDA531A6E44A70099D309 /* LoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A4FDA2A1A6E44270099D309 /* LoadingView.swift */; }; 16 | 1A4FDA541A6E44A70099D309 /* LoadingView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1A4FDA2B1A6E44270099D309 /* LoadingView.xib */; }; 17 | 1A4FDA551A6E44A70099D309 /* BlurView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888D31A66BF9000295A64 /* BlurView.swift */; }; 18 | 1A4FDA571A6E44A70099D309 /* DesignableButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888D51A66BF9000295A64 /* DesignableButton.swift */; }; 19 | 1A4FDA581A6E44A70099D309 /* DesignableImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888D61A66BF9000295A64 /* DesignableImageView.swift */; }; 20 | 1A4FDA591A6E44A70099D309 /* DesignableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888D71A66BF9000295A64 /* DesignableLabel.swift */; }; 21 | 1A4FDA5A1A6E44A70099D309 /* DesignableTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888D81A66BF9000295A64 /* DesignableTextField.swift */; }; 22 | 1A4FDA5B1A6E44A70099D309 /* DesignableTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888D91A66BF9000295A64 /* DesignableTextView.swift */; }; 23 | 1A4FDA5C1A6E44A70099D309 /* DesignableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888DA1A66BF9000295A64 /* DesignableView.swift */; }; 24 | 1A4FDA5D1A6E44A70099D309 /* DesignableTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 969775D31A6AD6AC009B4B79 /* DesignableTabBarController.swift */; }; 25 | 1A4FDA5E1A6E44A70099D309 /* ImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888DB1A66BF9000295A64 /* ImageLoader.swift */; }; 26 | 1A4FDA601A6E44A70099D309 /* Misc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888DF1A66BF9000295A64 /* Misc.swift */; }; 27 | 1A4FDA611A6E44A70099D309 /* Spring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD08AC61A676D5800160D45 /* Spring.swift */; }; 28 | 1A4FDA621A6E44A70099D309 /* SpringAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888E01A66BF9000295A64 /* SpringAnimation.swift */; }; 29 | 1A4FDA631A6E44A70099D309 /* SpringButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888E11A66BF9000295A64 /* SpringButton.swift */; }; 30 | 1A4FDA641A6E44A70099D309 /* SpringImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF3F11A1A6776760090E8F9 /* SpringImageView.swift */; }; 31 | 1A4FDA651A6E44A70099D309 /* SpringLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF3F11B1A6776760090E8F9 /* SpringLabel.swift */; }; 32 | 1A4FDA661A6E44A70099D309 /* SpringTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF3F11C1A6776760090E8F9 /* SpringTextField.swift */; }; 33 | 1A4FDA671A6E44A70099D309 /* SpringTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF3F11D1A6776760090E8F9 /* SpringTextView.swift */; }; 34 | 1A4FDA681A6E44A70099D309 /* SpringView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888E21A66BF9000295A64 /* SpringView.swift */; }; 35 | 1A4FDA691A6E44A70099D309 /* TransitionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888E31A66BF9000295A64 /* TransitionManager.swift */; }; 36 | 1A4FDA6A1A6E44A70099D309 /* TransitionZoom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888E41A66BF9000295A64 /* TransitionZoom.swift */; }; 37 | 1A4FDA6B1A6E44A70099D309 /* UnwindSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888E51A66BF9000295A64 /* UnwindSegue.swift */; }; 38 | 1A585F401A7B9530007EEB7D /* KeyboardLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A585F3F1A7B9530007EEB7D /* KeyboardLayoutConstraint.swift */; }; 39 | 1A9F866D1A83C5640098BE6C /* SoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9F866C1A83C5640098BE6C /* SoundPlayer.swift */; }; 40 | 1AA7E1831AA36EFF00762D75 /* AsyncImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AA7E1811AA36EFF00762D75 /* AsyncImageView.swift */; }; 41 | 1AA7E1841AA36EFF00762D75 /* AsyncButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AA7E1821AA36EFF00762D75 /* AsyncButton.swift */; }; 42 | 1AB764641A6E4F070076CD78 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 964117471A5BE90A000E3A5A /* Images.xcassets */; }; 43 | 964117411A5BE90A000E3A5A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964117401A5BE90A000E3A5A /* AppDelegate.swift */; }; 44 | 964117461A5BE90A000E3A5A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 964117441A5BE90A000E3A5A /* Main.storyboard */; }; 45 | 964117481A5BE90A000E3A5A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 964117471A5BE90A000E3A5A /* Images.xcassets */; }; 46 | 9641174B1A5BE90A000E3A5A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 964117491A5BE90A000E3A5A /* LaunchScreen.xib */; }; 47 | 964117571A5BE90A000E3A5A /* SpringAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964117561A5BE90A000E3A5A /* SpringAppTests.swift */; }; 48 | 9641178B1A5BEC6F000E3A5A /* SpringViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964117881A5BEC6F000E3A5A /* SpringViewController.swift */; }; 49 | 9641178C1A5BEC6F000E3A5A /* OptionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964117891A5BEC6F000E3A5A /* OptionsViewController.swift */; }; 50 | 9641178D1A5BEC6F000E3A5A /* CodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9641178A1A5BEC6F000E3A5A /* CodeViewController.swift */; }; 51 | 96C5F4621AC464C800BB8A18 /* AutoTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C5F4611AC464C800BB8A18 /* AutoTextView.swift */; }; 52 | /* End PBXBuildFile section */ 53 | 54 | /* Begin PBXContainerItemProxy section */ 55 | 1A4FDA3F1A6E44780099D309 /* PBXContainerItemProxy */ = { 56 | isa = PBXContainerItemProxy; 57 | containerPortal = 964117331A5BE90A000E3A5A /* Project object */; 58 | proxyType = 1; 59 | remoteGlobalIDString = 1A4FDA321A6E44780099D309; 60 | remoteInfo = Spring; 61 | }; 62 | 1A4FDA411A6E44780099D309 /* PBXContainerItemProxy */ = { 63 | isa = PBXContainerItemProxy; 64 | containerPortal = 964117331A5BE90A000E3A5A /* Project object */; 65 | proxyType = 1; 66 | remoteGlobalIDString = 9641173A1A5BE90A000E3A5A; 67 | remoteInfo = SpringApp; 68 | }; 69 | 1A4FDA481A6E44780099D309 /* PBXContainerItemProxy */ = { 70 | isa = PBXContainerItemProxy; 71 | containerPortal = 964117331A5BE90A000E3A5A /* Project object */; 72 | proxyType = 1; 73 | remoteGlobalIDString = 1A4FDA321A6E44780099D309; 74 | remoteInfo = Spring; 75 | }; 76 | 964117511A5BE90A000E3A5A /* PBXContainerItemProxy */ = { 77 | isa = PBXContainerItemProxy; 78 | containerPortal = 964117331A5BE90A000E3A5A /* Project object */; 79 | proxyType = 1; 80 | remoteGlobalIDString = 9641173A1A5BE90A000E3A5A; 81 | remoteInfo = SpringApp; 82 | }; 83 | /* End PBXContainerItemProxy section */ 84 | 85 | /* Begin PBXCopyFilesBuildPhase section */ 86 | 1A4FDA4F1A6E44780099D309 /* Embed Frameworks */ = { 87 | isa = PBXCopyFilesBuildPhase; 88 | buildActionMask = 2147483647; 89 | dstPath = ""; 90 | dstSubfolderSpec = 10; 91 | files = ( 92 | 1A4FDA4B1A6E44780099D309 /* Spring.framework in Embed Frameworks */, 93 | ); 94 | name = "Embed Frameworks"; 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | /* End PBXCopyFilesBuildPhase section */ 98 | 99 | /* Begin PBXFileReference section */ 100 | 1A4FDA2A1A6E44270099D309 /* LoadingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = ""; }; 101 | 1A4FDA2B1A6E44270099D309 /* LoadingView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LoadingView.xib; sourceTree = ""; }; 102 | 1A4FDA331A6E44780099D309 /* Spring.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Spring.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 103 | 1A4FDA361A6E44780099D309 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 104 | 1A4FDA371A6E44780099D309 /* Spring.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Spring.h; sourceTree = ""; }; 105 | 1A4FDA3D1A6E44780099D309 /* SpringTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SpringTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 106 | 1A4FDA451A6E44780099D309 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 107 | 1A4FDA461A6E44780099D309 /* SpringTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpringTests.swift; sourceTree = ""; }; 108 | 1A585F3F1A7B9530007EEB7D /* KeyboardLayoutConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardLayoutConstraint.swift; sourceTree = ""; }; 109 | 1A9F866C1A83C5640098BE6C /* SoundPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SoundPlayer.swift; sourceTree = ""; }; 110 | 1AA7E1811AA36EFF00762D75 /* AsyncImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncImageView.swift; sourceTree = ""; }; 111 | 1AA7E1821AA36EFF00762D75 /* AsyncButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncButton.swift; sourceTree = ""; }; 112 | 1AD08AC61A676D5800160D45 /* Spring.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Spring.swift; sourceTree = ""; }; 113 | 1AF3F11A1A6776760090E8F9 /* SpringImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringImageView.swift; sourceTree = ""; }; 114 | 1AF3F11B1A6776760090E8F9 /* SpringLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringLabel.swift; sourceTree = ""; }; 115 | 1AF3F11C1A6776760090E8F9 /* SpringTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringTextField.swift; sourceTree = ""; }; 116 | 1AF3F11D1A6776760090E8F9 /* SpringTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringTextView.swift; sourceTree = ""; }; 117 | 961888D31A66BF9000295A64 /* BlurView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlurView.swift; sourceTree = ""; }; 118 | 961888D51A66BF9000295A64 /* DesignableButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DesignableButton.swift; sourceTree = ""; }; 119 | 961888D61A66BF9000295A64 /* DesignableImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DesignableImageView.swift; sourceTree = ""; }; 120 | 961888D71A66BF9000295A64 /* DesignableLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DesignableLabel.swift; sourceTree = ""; }; 121 | 961888D81A66BF9000295A64 /* DesignableTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DesignableTextField.swift; sourceTree = ""; }; 122 | 961888D91A66BF9000295A64 /* DesignableTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DesignableTextView.swift; sourceTree = ""; }; 123 | 961888DA1A66BF9000295A64 /* DesignableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DesignableView.swift; sourceTree = ""; }; 124 | 961888DB1A66BF9000295A64 /* ImageLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageLoader.swift; sourceTree = ""; }; 125 | 961888DF1A66BF9000295A64 /* Misc.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Misc.swift; sourceTree = ""; }; 126 | 961888E01A66BF9000295A64 /* SpringAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringAnimation.swift; sourceTree = ""; }; 127 | 961888E11A66BF9000295A64 /* SpringButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringButton.swift; sourceTree = ""; }; 128 | 961888E21A66BF9000295A64 /* SpringView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringView.swift; sourceTree = ""; }; 129 | 961888E31A66BF9000295A64 /* TransitionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionManager.swift; sourceTree = ""; }; 130 | 961888E41A66BF9000295A64 /* TransitionZoom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionZoom.swift; sourceTree = ""; }; 131 | 961888E51A66BF9000295A64 /* UnwindSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnwindSegue.swift; sourceTree = ""; }; 132 | 9641173B1A5BE90A000E3A5A /* SpringApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SpringApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 133 | 9641173F1A5BE90A000E3A5A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 134 | 964117401A5BE90A000E3A5A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 135 | 964117451A5BE90A000E3A5A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 136 | 964117471A5BE90A000E3A5A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 137 | 9641174A1A5BE90A000E3A5A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 138 | 964117501A5BE90A000E3A5A /* SpringAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SpringAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 139 | 964117551A5BE90A000E3A5A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 140 | 964117561A5BE90A000E3A5A /* SpringAppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpringAppTests.swift; sourceTree = ""; }; 141 | 964117881A5BEC6F000E3A5A /* SpringViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringViewController.swift; sourceTree = ""; }; 142 | 964117891A5BEC6F000E3A5A /* OptionsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OptionsViewController.swift; sourceTree = ""; }; 143 | 9641178A1A5BEC6F000E3A5A /* CodeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodeViewController.swift; sourceTree = ""; }; 144 | 969775D31A6AD6AC009B4B79 /* DesignableTabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DesignableTabBarController.swift; sourceTree = ""; }; 145 | 96C5F4611AC464C800BB8A18 /* AutoTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutoTextView.swift; sourceTree = ""; }; 146 | /* End PBXFileReference section */ 147 | 148 | /* Begin PBXFrameworksBuildPhase section */ 149 | 1A4FDA2F1A6E44780099D309 /* Frameworks */ = { 150 | isa = PBXFrameworksBuildPhase; 151 | buildActionMask = 2147483647; 152 | files = ( 153 | ); 154 | runOnlyForDeploymentPostprocessing = 0; 155 | }; 156 | 1A4FDA3A1A6E44780099D309 /* Frameworks */ = { 157 | isa = PBXFrameworksBuildPhase; 158 | buildActionMask = 2147483647; 159 | files = ( 160 | 1A4FDA3E1A6E44780099D309 /* Spring.framework in Frameworks */, 161 | ); 162 | runOnlyForDeploymentPostprocessing = 0; 163 | }; 164 | 964117381A5BE90A000E3A5A /* Frameworks */ = { 165 | isa = PBXFrameworksBuildPhase; 166 | buildActionMask = 2147483647; 167 | files = ( 168 | 1A4FDA4A1A6E44780099D309 /* Spring.framework in Frameworks */, 169 | ); 170 | runOnlyForDeploymentPostprocessing = 0; 171 | }; 172 | 9641174D1A5BE90A000E3A5A /* Frameworks */ = { 173 | isa = PBXFrameworksBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | ); 177 | runOnlyForDeploymentPostprocessing = 0; 178 | }; 179 | /* End PBXFrameworksBuildPhase section */ 180 | 181 | /* Begin PBXGroup section */ 182 | 1A4FDA341A6E44780099D309 /* Spring */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | 1A4FDA371A6E44780099D309 /* Spring.h */, 186 | 1A4FDA351A6E44780099D309 /* Supporting Files */, 187 | ); 188 | path = Spring; 189 | sourceTree = ""; 190 | }; 191 | 1A4FDA351A6E44780099D309 /* Supporting Files */ = { 192 | isa = PBXGroup; 193 | children = ( 194 | 1A4FDA361A6E44780099D309 /* Info.plist */, 195 | ); 196 | name = "Supporting Files"; 197 | sourceTree = ""; 198 | }; 199 | 1A4FDA431A6E44780099D309 /* SpringTests */ = { 200 | isa = PBXGroup; 201 | children = ( 202 | 1A4FDA461A6E44780099D309 /* SpringTests.swift */, 203 | 1A4FDA441A6E44780099D309 /* Supporting Files */, 204 | ); 205 | path = SpringTests; 206 | sourceTree = ""; 207 | }; 208 | 1A4FDA441A6E44780099D309 /* Supporting Files */ = { 209 | isa = PBXGroup; 210 | children = ( 211 | 1A4FDA451A6E44780099D309 /* Info.plist */, 212 | ); 213 | name = "Supporting Files"; 214 | sourceTree = ""; 215 | }; 216 | 961888D21A66BF9000295A64 /* Spring */ = { 217 | isa = PBXGroup; 218 | children = ( 219 | 1AA7E1821AA36EFF00762D75 /* AsyncButton.swift */, 220 | 1AA7E1811AA36EFF00762D75 /* AsyncImageView.swift */, 221 | 96C5F4611AC464C800BB8A18 /* AutoTextView.swift */, 222 | 1A4FDA2A1A6E44270099D309 /* LoadingView.swift */, 223 | 1A4FDA2B1A6E44270099D309 /* LoadingView.xib */, 224 | 961888D31A66BF9000295A64 /* BlurView.swift */, 225 | 961888D51A66BF9000295A64 /* DesignableButton.swift */, 226 | 961888D61A66BF9000295A64 /* DesignableImageView.swift */, 227 | 961888D71A66BF9000295A64 /* DesignableLabel.swift */, 228 | 961888D81A66BF9000295A64 /* DesignableTextField.swift */, 229 | 961888D91A66BF9000295A64 /* DesignableTextView.swift */, 230 | 961888DA1A66BF9000295A64 /* DesignableView.swift */, 231 | 969775D31A6AD6AC009B4B79 /* DesignableTabBarController.swift */, 232 | 961888DB1A66BF9000295A64 /* ImageLoader.swift */, 233 | 1A585F3F1A7B9530007EEB7D /* KeyboardLayoutConstraint.swift */, 234 | 961888DF1A66BF9000295A64 /* Misc.swift */, 235 | 1A9F866C1A83C5640098BE6C /* SoundPlayer.swift */, 236 | 1AD08AC61A676D5800160D45 /* Spring.swift */, 237 | 961888E01A66BF9000295A64 /* SpringAnimation.swift */, 238 | 961888E11A66BF9000295A64 /* SpringButton.swift */, 239 | 1AF3F11A1A6776760090E8F9 /* SpringImageView.swift */, 240 | 1AF3F11B1A6776760090E8F9 /* SpringLabel.swift */, 241 | 1AF3F11C1A6776760090E8F9 /* SpringTextField.swift */, 242 | 1AF3F11D1A6776760090E8F9 /* SpringTextView.swift */, 243 | 961888E21A66BF9000295A64 /* SpringView.swift */, 244 | 961888E31A66BF9000295A64 /* TransitionManager.swift */, 245 | 961888E41A66BF9000295A64 /* TransitionZoom.swift */, 246 | 961888E51A66BF9000295A64 /* UnwindSegue.swift */, 247 | ); 248 | path = Spring; 249 | sourceTree = SOURCE_ROOT; 250 | }; 251 | 964117321A5BE90A000E3A5A = { 252 | isa = PBXGroup; 253 | children = ( 254 | 9641173D1A5BE90A000E3A5A /* SpringApp */, 255 | 964117531A5BE90A000E3A5A /* SpringAppTests */, 256 | 1A4FDA341A6E44780099D309 /* Spring */, 257 | 1A4FDA431A6E44780099D309 /* SpringTests */, 258 | 9641173C1A5BE90A000E3A5A /* Products */, 259 | ); 260 | indentWidth = 4; 261 | sourceTree = ""; 262 | tabWidth = 4; 263 | }; 264 | 9641173C1A5BE90A000E3A5A /* Products */ = { 265 | isa = PBXGroup; 266 | children = ( 267 | 9641173B1A5BE90A000E3A5A /* SpringApp.app */, 268 | 964117501A5BE90A000E3A5A /* SpringAppTests.xctest */, 269 | 1A4FDA331A6E44780099D309 /* Spring.framework */, 270 | 1A4FDA3D1A6E44780099D309 /* SpringTests.xctest */, 271 | ); 272 | name = Products; 273 | sourceTree = ""; 274 | }; 275 | 9641173D1A5BE90A000E3A5A /* SpringApp */ = { 276 | isa = PBXGroup; 277 | children = ( 278 | 961888D21A66BF9000295A64 /* Spring */, 279 | 964117401A5BE90A000E3A5A /* AppDelegate.swift */, 280 | 964117881A5BEC6F000E3A5A /* SpringViewController.swift */, 281 | 964117891A5BEC6F000E3A5A /* OptionsViewController.swift */, 282 | 9641178A1A5BEC6F000E3A5A /* CodeViewController.swift */, 283 | 964117441A5BE90A000E3A5A /* Main.storyboard */, 284 | 964117471A5BE90A000E3A5A /* Images.xcassets */, 285 | 964117491A5BE90A000E3A5A /* LaunchScreen.xib */, 286 | 9641173E1A5BE90A000E3A5A /* Supporting Files */, 287 | ); 288 | path = SpringApp; 289 | sourceTree = ""; 290 | }; 291 | 9641173E1A5BE90A000E3A5A /* Supporting Files */ = { 292 | isa = PBXGroup; 293 | children = ( 294 | 9641173F1A5BE90A000E3A5A /* Info.plist */, 295 | ); 296 | name = "Supporting Files"; 297 | sourceTree = ""; 298 | }; 299 | 964117531A5BE90A000E3A5A /* SpringAppTests */ = { 300 | isa = PBXGroup; 301 | children = ( 302 | 964117561A5BE90A000E3A5A /* SpringAppTests.swift */, 303 | 964117541A5BE90A000E3A5A /* Supporting Files */, 304 | ); 305 | path = SpringAppTests; 306 | sourceTree = ""; 307 | }; 308 | 964117541A5BE90A000E3A5A /* Supporting Files */ = { 309 | isa = PBXGroup; 310 | children = ( 311 | 964117551A5BE90A000E3A5A /* Info.plist */, 312 | ); 313 | name = "Supporting Files"; 314 | sourceTree = ""; 315 | }; 316 | /* End PBXGroup section */ 317 | 318 | /* Begin PBXHeadersBuildPhase section */ 319 | 1A4FDA301A6E44780099D309 /* Headers */ = { 320 | isa = PBXHeadersBuildPhase; 321 | buildActionMask = 2147483647; 322 | files = ( 323 | 1A4FDA381A6E44780099D309 /* Spring.h in Headers */, 324 | ); 325 | runOnlyForDeploymentPostprocessing = 0; 326 | }; 327 | /* End PBXHeadersBuildPhase section */ 328 | 329 | /* Begin PBXNativeTarget section */ 330 | 1A4FDA321A6E44780099D309 /* Spring */ = { 331 | isa = PBXNativeTarget; 332 | buildConfigurationList = 1A4FDA4C1A6E44780099D309 /* Build configuration list for PBXNativeTarget "Spring" */; 333 | buildPhases = ( 334 | 1A4FDA2E1A6E44780099D309 /* Sources */, 335 | 1A4FDA2F1A6E44780099D309 /* Frameworks */, 336 | 1A4FDA301A6E44780099D309 /* Headers */, 337 | 1A4FDA311A6E44780099D309 /* Resources */, 338 | ); 339 | buildRules = ( 340 | ); 341 | dependencies = ( 342 | ); 343 | name = Spring; 344 | productName = Spring; 345 | productReference = 1A4FDA331A6E44780099D309 /* Spring.framework */; 346 | productType = "com.apple.product-type.framework"; 347 | }; 348 | 1A4FDA3C1A6E44780099D309 /* SpringTests */ = { 349 | isa = PBXNativeTarget; 350 | buildConfigurationList = 1A4FDA501A6E44780099D309 /* Build configuration list for PBXNativeTarget "SpringTests" */; 351 | buildPhases = ( 352 | 1A4FDA391A6E44780099D309 /* Sources */, 353 | 1A4FDA3A1A6E44780099D309 /* Frameworks */, 354 | 1A4FDA3B1A6E44780099D309 /* Resources */, 355 | ); 356 | buildRules = ( 357 | ); 358 | dependencies = ( 359 | 1A4FDA401A6E44780099D309 /* PBXTargetDependency */, 360 | 1A4FDA421A6E44780099D309 /* PBXTargetDependency */, 361 | ); 362 | name = SpringTests; 363 | productName = SpringTests; 364 | productReference = 1A4FDA3D1A6E44780099D309 /* SpringTests.xctest */; 365 | productType = "com.apple.product-type.bundle.unit-test"; 366 | }; 367 | 9641173A1A5BE90A000E3A5A /* SpringApp */ = { 368 | isa = PBXNativeTarget; 369 | buildConfigurationList = 9641175A1A5BE90A000E3A5A /* Build configuration list for PBXNativeTarget "SpringApp" */; 370 | buildPhases = ( 371 | 964117371A5BE90A000E3A5A /* Sources */, 372 | 964117381A5BE90A000E3A5A /* Frameworks */, 373 | 964117391A5BE90A000E3A5A /* Resources */, 374 | 1A4FDA4F1A6E44780099D309 /* Embed Frameworks */, 375 | ); 376 | buildRules = ( 377 | ); 378 | dependencies = ( 379 | 1A4FDA491A6E44780099D309 /* PBXTargetDependency */, 380 | ); 381 | name = SpringApp; 382 | productName = SpringApp; 383 | productReference = 9641173B1A5BE90A000E3A5A /* SpringApp.app */; 384 | productType = "com.apple.product-type.application"; 385 | }; 386 | 9641174F1A5BE90A000E3A5A /* SpringAppTests */ = { 387 | isa = PBXNativeTarget; 388 | buildConfigurationList = 9641175D1A5BE90A000E3A5A /* Build configuration list for PBXNativeTarget "SpringAppTests" */; 389 | buildPhases = ( 390 | 9641174C1A5BE90A000E3A5A /* Sources */, 391 | 9641174D1A5BE90A000E3A5A /* Frameworks */, 392 | 9641174E1A5BE90A000E3A5A /* Resources */, 393 | ); 394 | buildRules = ( 395 | ); 396 | dependencies = ( 397 | 964117521A5BE90A000E3A5A /* PBXTargetDependency */, 398 | ); 399 | name = SpringAppTests; 400 | productName = SpringAppTests; 401 | productReference = 964117501A5BE90A000E3A5A /* SpringAppTests.xctest */; 402 | productType = "com.apple.product-type.bundle.unit-test"; 403 | }; 404 | /* End PBXNativeTarget section */ 405 | 406 | /* Begin PBXProject section */ 407 | 964117331A5BE90A000E3A5A /* Project object */ = { 408 | isa = PBXProject; 409 | attributes = { 410 | LastSwiftUpdateCheck = 0700; 411 | LastUpgradeCheck = 0900; 412 | ORGANIZATIONNAME = "Meng To"; 413 | TargetAttributes = { 414 | 1A4FDA321A6E44780099D309 = { 415 | CreatedOnToolsVersion = 6.1.1; 416 | LastSwiftMigration = 1020; 417 | }; 418 | 1A4FDA3C1A6E44780099D309 = { 419 | CreatedOnToolsVersion = 6.1.1; 420 | LastSwiftMigration = 1020; 421 | TestTargetID = 9641173A1A5BE90A000E3A5A; 422 | }; 423 | 9641173A1A5BE90A000E3A5A = { 424 | CreatedOnToolsVersion = 6.2; 425 | LastSwiftMigration = 1020; 426 | }; 427 | 9641174F1A5BE90A000E3A5A = { 428 | CreatedOnToolsVersion = 6.2; 429 | LastSwiftMigration = 1020; 430 | TestTargetID = 9641173A1A5BE90A000E3A5A; 431 | }; 432 | }; 433 | }; 434 | buildConfigurationList = 964117361A5BE90A000E3A5A /* Build configuration list for PBXProject "SpringApp" */; 435 | compatibilityVersion = "Xcode 3.2"; 436 | developmentRegion = English; 437 | hasScannedForEncodings = 0; 438 | knownRegions = ( 439 | English, 440 | en, 441 | Base, 442 | ); 443 | mainGroup = 964117321A5BE90A000E3A5A; 444 | productRefGroup = 9641173C1A5BE90A000E3A5A /* Products */; 445 | projectDirPath = ""; 446 | projectRoot = ""; 447 | targets = ( 448 | 9641173A1A5BE90A000E3A5A /* SpringApp */, 449 | 9641174F1A5BE90A000E3A5A /* SpringAppTests */, 450 | 1A4FDA321A6E44780099D309 /* Spring */, 451 | 1A4FDA3C1A6E44780099D309 /* SpringTests */, 452 | ); 453 | }; 454 | /* End PBXProject section */ 455 | 456 | /* Begin PBXResourcesBuildPhase section */ 457 | 1A4FDA311A6E44780099D309 /* Resources */ = { 458 | isa = PBXResourcesBuildPhase; 459 | buildActionMask = 2147483647; 460 | files = ( 461 | 1AB764641A6E4F070076CD78 /* Images.xcassets in Resources */, 462 | 1A4FDA541A6E44A70099D309 /* LoadingView.xib in Resources */, 463 | ); 464 | runOnlyForDeploymentPostprocessing = 0; 465 | }; 466 | 1A4FDA3B1A6E44780099D309 /* Resources */ = { 467 | isa = PBXResourcesBuildPhase; 468 | buildActionMask = 2147483647; 469 | files = ( 470 | ); 471 | runOnlyForDeploymentPostprocessing = 0; 472 | }; 473 | 964117391A5BE90A000E3A5A /* Resources */ = { 474 | isa = PBXResourcesBuildPhase; 475 | buildActionMask = 2147483647; 476 | files = ( 477 | 964117461A5BE90A000E3A5A /* Main.storyboard in Resources */, 478 | 9641174B1A5BE90A000E3A5A /* LaunchScreen.xib in Resources */, 479 | 964117481A5BE90A000E3A5A /* Images.xcassets in Resources */, 480 | ); 481 | runOnlyForDeploymentPostprocessing = 0; 482 | }; 483 | 9641174E1A5BE90A000E3A5A /* Resources */ = { 484 | isa = PBXResourcesBuildPhase; 485 | buildActionMask = 2147483647; 486 | files = ( 487 | ); 488 | runOnlyForDeploymentPostprocessing = 0; 489 | }; 490 | /* End PBXResourcesBuildPhase section */ 491 | 492 | /* Begin PBXSourcesBuildPhase section */ 493 | 1A4FDA2E1A6E44780099D309 /* Sources */ = { 494 | isa = PBXSourcesBuildPhase; 495 | buildActionMask = 2147483647; 496 | files = ( 497 | 1A4FDA531A6E44A70099D309 /* LoadingView.swift in Sources */, 498 | 1A4FDA5B1A6E44A70099D309 /* DesignableTextView.swift in Sources */, 499 | 1A4FDA671A6E44A70099D309 /* SpringTextView.swift in Sources */, 500 | 1A4FDA641A6E44A70099D309 /* SpringImageView.swift in Sources */, 501 | 1A4FDA581A6E44A70099D309 /* DesignableImageView.swift in Sources */, 502 | 1A4FDA601A6E44A70099D309 /* Misc.swift in Sources */, 503 | 1A4FDA611A6E44A70099D309 /* Spring.swift in Sources */, 504 | 1A4FDA5A1A6E44A70099D309 /* DesignableTextField.swift in Sources */, 505 | 1A4FDA691A6E44A70099D309 /* TransitionManager.swift in Sources */, 506 | 1A4FDA5E1A6E44A70099D309 /* ImageLoader.swift in Sources */, 507 | 1A9F866D1A83C5640098BE6C /* SoundPlayer.swift in Sources */, 508 | 1A4FDA551A6E44A70099D309 /* BlurView.swift in Sources */, 509 | 1AA7E1841AA36EFF00762D75 /* AsyncButton.swift in Sources */, 510 | 1A4FDA5D1A6E44A70099D309 /* DesignableTabBarController.swift in Sources */, 511 | 1A4FDA591A6E44A70099D309 /* DesignableLabel.swift in Sources */, 512 | 1A4FDA661A6E44A70099D309 /* SpringTextField.swift in Sources */, 513 | 1A4FDA631A6E44A70099D309 /* SpringButton.swift in Sources */, 514 | 1A4FDA621A6E44A70099D309 /* SpringAnimation.swift in Sources */, 515 | 1A585F401A7B9530007EEB7D /* KeyboardLayoutConstraint.swift in Sources */, 516 | 1AA7E1831AA36EFF00762D75 /* AsyncImageView.swift in Sources */, 517 | 1A4FDA6B1A6E44A70099D309 /* UnwindSegue.swift in Sources */, 518 | 1A4FDA681A6E44A70099D309 /* SpringView.swift in Sources */, 519 | 1A4FDA6A1A6E44A70099D309 /* TransitionZoom.swift in Sources */, 520 | 1A4FDA651A6E44A70099D309 /* SpringLabel.swift in Sources */, 521 | 1A4FDA571A6E44A70099D309 /* DesignableButton.swift in Sources */, 522 | 1A4FDA5C1A6E44A70099D309 /* DesignableView.swift in Sources */, 523 | ); 524 | runOnlyForDeploymentPostprocessing = 0; 525 | }; 526 | 1A4FDA391A6E44780099D309 /* Sources */ = { 527 | isa = PBXSourcesBuildPhase; 528 | buildActionMask = 2147483647; 529 | files = ( 530 | 1A4FDA471A6E44780099D309 /* SpringTests.swift in Sources */, 531 | ); 532 | runOnlyForDeploymentPostprocessing = 0; 533 | }; 534 | 964117371A5BE90A000E3A5A /* Sources */ = { 535 | isa = PBXSourcesBuildPhase; 536 | buildActionMask = 2147483647; 537 | files = ( 538 | 964117411A5BE90A000E3A5A /* AppDelegate.swift in Sources */, 539 | 9641178D1A5BEC6F000E3A5A /* CodeViewController.swift in Sources */, 540 | 9641178C1A5BEC6F000E3A5A /* OptionsViewController.swift in Sources */, 541 | 9641178B1A5BEC6F000E3A5A /* SpringViewController.swift in Sources */, 542 | 96C5F4621AC464C800BB8A18 /* AutoTextView.swift in Sources */, 543 | ); 544 | runOnlyForDeploymentPostprocessing = 0; 545 | }; 546 | 9641174C1A5BE90A000E3A5A /* Sources */ = { 547 | isa = PBXSourcesBuildPhase; 548 | buildActionMask = 2147483647; 549 | files = ( 550 | 964117571A5BE90A000E3A5A /* SpringAppTests.swift in Sources */, 551 | ); 552 | runOnlyForDeploymentPostprocessing = 0; 553 | }; 554 | /* End PBXSourcesBuildPhase section */ 555 | 556 | /* Begin PBXTargetDependency section */ 557 | 1A4FDA401A6E44780099D309 /* PBXTargetDependency */ = { 558 | isa = PBXTargetDependency; 559 | target = 1A4FDA321A6E44780099D309 /* Spring */; 560 | targetProxy = 1A4FDA3F1A6E44780099D309 /* PBXContainerItemProxy */; 561 | }; 562 | 1A4FDA421A6E44780099D309 /* PBXTargetDependency */ = { 563 | isa = PBXTargetDependency; 564 | target = 9641173A1A5BE90A000E3A5A /* SpringApp */; 565 | targetProxy = 1A4FDA411A6E44780099D309 /* PBXContainerItemProxy */; 566 | }; 567 | 1A4FDA491A6E44780099D309 /* PBXTargetDependency */ = { 568 | isa = PBXTargetDependency; 569 | target = 1A4FDA321A6E44780099D309 /* Spring */; 570 | targetProxy = 1A4FDA481A6E44780099D309 /* PBXContainerItemProxy */; 571 | }; 572 | 964117521A5BE90A000E3A5A /* PBXTargetDependency */ = { 573 | isa = PBXTargetDependency; 574 | target = 9641173A1A5BE90A000E3A5A /* SpringApp */; 575 | targetProxy = 964117511A5BE90A000E3A5A /* PBXContainerItemProxy */; 576 | }; 577 | /* End PBXTargetDependency section */ 578 | 579 | /* Begin PBXVariantGroup section */ 580 | 964117441A5BE90A000E3A5A /* Main.storyboard */ = { 581 | isa = PBXVariantGroup; 582 | children = ( 583 | 964117451A5BE90A000E3A5A /* Base */, 584 | ); 585 | name = Main.storyboard; 586 | sourceTree = ""; 587 | }; 588 | 964117491A5BE90A000E3A5A /* LaunchScreen.xib */ = { 589 | isa = PBXVariantGroup; 590 | children = ( 591 | 9641174A1A5BE90A000E3A5A /* Base */, 592 | ); 593 | name = LaunchScreen.xib; 594 | sourceTree = ""; 595 | }; 596 | /* End PBXVariantGroup section */ 597 | 598 | /* Begin XCBuildConfiguration section */ 599 | 1A4FDA4D1A6E44780099D309 /* Debug */ = { 600 | isa = XCBuildConfiguration; 601 | buildSettings = { 602 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 603 | CURRENT_PROJECT_VERSION = 1; 604 | DEFINES_MODULE = YES; 605 | DYLIB_COMPATIBILITY_VERSION = 1; 606 | DYLIB_CURRENT_VERSION = 1; 607 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 608 | GCC_PREPROCESSOR_DEFINITIONS = ( 609 | "DEBUG=1", 610 | "$(inherited)", 611 | ); 612 | INFOPLIST_FILE = Spring/Info.plist; 613 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 614 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 615 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 616 | PRODUCT_BUNDLE_IDENTIFIER = "designcode.$(PRODUCT_NAME:rfc1034identifier)"; 617 | PRODUCT_NAME = "$(TARGET_NAME)"; 618 | SKIP_INSTALL = YES; 619 | SWIFT_VERSION = 5.0; 620 | TARGETED_DEVICE_FAMILY = "1,2"; 621 | VERSIONING_SYSTEM = "apple-generic"; 622 | VERSION_INFO_PREFIX = ""; 623 | }; 624 | name = Debug; 625 | }; 626 | 1A4FDA4E1A6E44780099D309 /* Release */ = { 627 | isa = XCBuildConfiguration; 628 | buildSettings = { 629 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 630 | CURRENT_PROJECT_VERSION = 1; 631 | DEFINES_MODULE = YES; 632 | DYLIB_COMPATIBILITY_VERSION = 1; 633 | DYLIB_CURRENT_VERSION = 1; 634 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 635 | INFOPLIST_FILE = Spring/Info.plist; 636 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 637 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 638 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 639 | PRODUCT_BUNDLE_IDENTIFIER = "designcode.$(PRODUCT_NAME:rfc1034identifier)"; 640 | PRODUCT_NAME = "$(TARGET_NAME)"; 641 | SKIP_INSTALL = YES; 642 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 643 | SWIFT_VERSION = 5.0; 644 | TARGETED_DEVICE_FAMILY = "1,2"; 645 | VERSIONING_SYSTEM = "apple-generic"; 646 | VERSION_INFO_PREFIX = ""; 647 | }; 648 | name = Release; 649 | }; 650 | 1A4FDA511A6E44780099D309 /* Debug */ = { 651 | isa = XCBuildConfiguration; 652 | buildSettings = { 653 | GCC_PREPROCESSOR_DEFINITIONS = ( 654 | "DEBUG=1", 655 | "$(inherited)", 656 | ); 657 | INFOPLIST_FILE = SpringTests/Info.plist; 658 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 659 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 660 | PRODUCT_BUNDLE_IDENTIFIER = "com.jamztang.$(PRODUCT_NAME:rfc1034identifier)"; 661 | PRODUCT_NAME = "$(TARGET_NAME)"; 662 | SWIFT_VERSION = 5.0; 663 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SpringApp.app/SpringApp"; 664 | }; 665 | name = Debug; 666 | }; 667 | 1A4FDA521A6E44780099D309 /* Release */ = { 668 | isa = XCBuildConfiguration; 669 | buildSettings = { 670 | INFOPLIST_FILE = SpringTests/Info.plist; 671 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 672 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 673 | PRODUCT_BUNDLE_IDENTIFIER = "com.jamztang.$(PRODUCT_NAME:rfc1034identifier)"; 674 | PRODUCT_NAME = "$(TARGET_NAME)"; 675 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 676 | SWIFT_VERSION = 5.0; 677 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SpringApp.app/SpringApp"; 678 | }; 679 | name = Release; 680 | }; 681 | 964117581A5BE90A000E3A5A /* Debug */ = { 682 | isa = XCBuildConfiguration; 683 | buildSettings = { 684 | ALWAYS_SEARCH_USER_PATHS = NO; 685 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 686 | CLANG_CXX_LIBRARY = "libc++"; 687 | CLANG_ENABLE_MODULES = YES; 688 | CLANG_ENABLE_OBJC_ARC = YES; 689 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 690 | CLANG_WARN_BOOL_CONVERSION = YES; 691 | CLANG_WARN_COMMA = YES; 692 | CLANG_WARN_CONSTANT_CONVERSION = YES; 693 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 694 | CLANG_WARN_EMPTY_BODY = YES; 695 | CLANG_WARN_ENUM_CONVERSION = YES; 696 | CLANG_WARN_INFINITE_RECURSION = YES; 697 | CLANG_WARN_INT_CONVERSION = YES; 698 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 699 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 700 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 701 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 702 | CLANG_WARN_STRICT_PROTOTYPES = YES; 703 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 704 | CLANG_WARN_UNREACHABLE_CODE = YES; 705 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 706 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 707 | COPY_PHASE_STRIP = NO; 708 | ENABLE_STRICT_OBJC_MSGSEND = YES; 709 | ENABLE_TESTABILITY = YES; 710 | GCC_C_LANGUAGE_STANDARD = gnu99; 711 | GCC_DYNAMIC_NO_PIC = NO; 712 | GCC_NO_COMMON_BLOCKS = YES; 713 | GCC_OPTIMIZATION_LEVEL = 0; 714 | GCC_PREPROCESSOR_DEFINITIONS = ( 715 | "DEBUG=1", 716 | "$(inherited)", 717 | ); 718 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 719 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 720 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 721 | GCC_WARN_UNDECLARED_SELECTOR = YES; 722 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 723 | GCC_WARN_UNUSED_FUNCTION = YES; 724 | GCC_WARN_UNUSED_VARIABLE = YES; 725 | IPHONEOS_DEPLOYMENT_TARGET = 8.2; 726 | MTL_ENABLE_DEBUG_INFO = YES; 727 | ONLY_ACTIVE_ARCH = YES; 728 | SDKROOT = iphoneos; 729 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 730 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 731 | }; 732 | name = Debug; 733 | }; 734 | 964117591A5BE90A000E3A5A /* Release */ = { 735 | isa = XCBuildConfiguration; 736 | buildSettings = { 737 | ALWAYS_SEARCH_USER_PATHS = NO; 738 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 739 | CLANG_CXX_LIBRARY = "libc++"; 740 | CLANG_ENABLE_MODULES = YES; 741 | CLANG_ENABLE_OBJC_ARC = YES; 742 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 743 | CLANG_WARN_BOOL_CONVERSION = YES; 744 | CLANG_WARN_COMMA = YES; 745 | CLANG_WARN_CONSTANT_CONVERSION = YES; 746 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 747 | CLANG_WARN_EMPTY_BODY = YES; 748 | CLANG_WARN_ENUM_CONVERSION = YES; 749 | CLANG_WARN_INFINITE_RECURSION = YES; 750 | CLANG_WARN_INT_CONVERSION = YES; 751 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 752 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 753 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 754 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 755 | CLANG_WARN_STRICT_PROTOTYPES = YES; 756 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 757 | CLANG_WARN_UNREACHABLE_CODE = YES; 758 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 759 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 760 | COPY_PHASE_STRIP = YES; 761 | ENABLE_NS_ASSERTIONS = NO; 762 | ENABLE_STRICT_OBJC_MSGSEND = YES; 763 | GCC_C_LANGUAGE_STANDARD = gnu99; 764 | GCC_NO_COMMON_BLOCKS = YES; 765 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 766 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 767 | GCC_WARN_UNDECLARED_SELECTOR = YES; 768 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 769 | GCC_WARN_UNUSED_FUNCTION = YES; 770 | GCC_WARN_UNUSED_VARIABLE = YES; 771 | IPHONEOS_DEPLOYMENT_TARGET = 8.2; 772 | MTL_ENABLE_DEBUG_INFO = NO; 773 | SDKROOT = iphoneos; 774 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 775 | VALIDATE_PRODUCT = YES; 776 | }; 777 | name = Release; 778 | }; 779 | 9641175B1A5BE90A000E3A5A /* Debug */ = { 780 | isa = XCBuildConfiguration; 781 | buildSettings = { 782 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 783 | INFOPLIST_FILE = SpringApp/Info.plist; 784 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 785 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 786 | PRODUCT_BUNDLE_IDENTIFIER = "designcode.$(PRODUCT_NAME:rfc1034identifier)"; 787 | PRODUCT_NAME = "$(TARGET_NAME)"; 788 | SWIFT_VERSION = 5.0; 789 | TARGETED_DEVICE_FAMILY = "1,2"; 790 | }; 791 | name = Debug; 792 | }; 793 | 9641175C1A5BE90A000E3A5A /* Release */ = { 794 | isa = XCBuildConfiguration; 795 | buildSettings = { 796 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 797 | INFOPLIST_FILE = SpringApp/Info.plist; 798 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 799 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 800 | PRODUCT_BUNDLE_IDENTIFIER = "designcode.$(PRODUCT_NAME:rfc1034identifier)"; 801 | PRODUCT_NAME = "$(TARGET_NAME)"; 802 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 803 | SWIFT_VERSION = 5.0; 804 | TARGETED_DEVICE_FAMILY = "1,2"; 805 | }; 806 | name = Release; 807 | }; 808 | 9641175E1A5BE90A000E3A5A /* Debug */ = { 809 | isa = XCBuildConfiguration; 810 | buildSettings = { 811 | BUNDLE_LOADER = "$(TEST_HOST)"; 812 | GCC_PREPROCESSOR_DEFINITIONS = ( 813 | "DEBUG=1", 814 | "$(inherited)", 815 | ); 816 | INFOPLIST_FILE = SpringAppTests/Info.plist; 817 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 818 | PRODUCT_BUNDLE_IDENTIFIER = "designcode.$(PRODUCT_NAME:rfc1034identifier)"; 819 | PRODUCT_NAME = "$(TARGET_NAME)"; 820 | SWIFT_VERSION = 5.0; 821 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SpringApp.app/SpringApp"; 822 | }; 823 | name = Debug; 824 | }; 825 | 9641175F1A5BE90A000E3A5A /* Release */ = { 826 | isa = XCBuildConfiguration; 827 | buildSettings = { 828 | BUNDLE_LOADER = "$(TEST_HOST)"; 829 | INFOPLIST_FILE = SpringAppTests/Info.plist; 830 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 831 | PRODUCT_BUNDLE_IDENTIFIER = "designcode.$(PRODUCT_NAME:rfc1034identifier)"; 832 | PRODUCT_NAME = "$(TARGET_NAME)"; 833 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 834 | SWIFT_VERSION = 5.0; 835 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SpringApp.app/SpringApp"; 836 | }; 837 | name = Release; 838 | }; 839 | /* End XCBuildConfiguration section */ 840 | 841 | /* Begin XCConfigurationList section */ 842 | 1A4FDA4C1A6E44780099D309 /* Build configuration list for PBXNativeTarget "Spring" */ = { 843 | isa = XCConfigurationList; 844 | buildConfigurations = ( 845 | 1A4FDA4D1A6E44780099D309 /* Debug */, 846 | 1A4FDA4E1A6E44780099D309 /* Release */, 847 | ); 848 | defaultConfigurationIsVisible = 0; 849 | defaultConfigurationName = Release; 850 | }; 851 | 1A4FDA501A6E44780099D309 /* Build configuration list for PBXNativeTarget "SpringTests" */ = { 852 | isa = XCConfigurationList; 853 | buildConfigurations = ( 854 | 1A4FDA511A6E44780099D309 /* Debug */, 855 | 1A4FDA521A6E44780099D309 /* Release */, 856 | ); 857 | defaultConfigurationIsVisible = 0; 858 | defaultConfigurationName = Release; 859 | }; 860 | 964117361A5BE90A000E3A5A /* Build configuration list for PBXProject "SpringApp" */ = { 861 | isa = XCConfigurationList; 862 | buildConfigurations = ( 863 | 964117581A5BE90A000E3A5A /* Debug */, 864 | 964117591A5BE90A000E3A5A /* Release */, 865 | ); 866 | defaultConfigurationIsVisible = 0; 867 | defaultConfigurationName = Release; 868 | }; 869 | 9641175A1A5BE90A000E3A5A /* Build configuration list for PBXNativeTarget "SpringApp" */ = { 870 | isa = XCConfigurationList; 871 | buildConfigurations = ( 872 | 9641175B1A5BE90A000E3A5A /* Debug */, 873 | 9641175C1A5BE90A000E3A5A /* Release */, 874 | ); 875 | defaultConfigurationIsVisible = 0; 876 | defaultConfigurationName = Release; 877 | }; 878 | 9641175D1A5BE90A000E3A5A /* Build configuration list for PBXNativeTarget "SpringAppTests" */ = { 879 | isa = XCConfigurationList; 880 | buildConfigurations = ( 881 | 9641175E1A5BE90A000E3A5A /* Debug */, 882 | 9641175F1A5BE90A000E3A5A /* Release */, 883 | ); 884 | defaultConfigurationIsVisible = 0; 885 | defaultConfigurationName = Release; 886 | }; 887 | /* End XCConfigurationList section */ 888 | }; 889 | rootObject = 964117331A5BE90A000E3A5A /* Project object */; 890 | } 891 | -------------------------------------------------------------------------------- /SpringApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SpringApp.xcodeproj/project.xcworkspace/xcshareddata/SpringApp.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 90BE4A8A-3AD7-483D-B9EA-ED8463875CE6 9 | IDESourceControlProjectName 10 | SpringApp 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 71F5D8D885CFF6BD7151066D7BFF25CC48A5235E 14 | github.com:MengTo/Spring.git 15 | 16 | IDESourceControlProjectPath 17 | SpringApp.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 71F5D8D885CFF6BD7151066D7BFF25CC48A5235E 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | github.com:MengTo/Spring.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 71F5D8D885CFF6BD7151066D7BFF25CC48A5235E 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 71F5D8D885CFF6BD7151066D7BFF25CC48A5235E 36 | IDESourceControlWCCName 37 | Spring 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /SpringApp.xcodeproj/xcshareddata/xcschemes/Spring.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /SpringApp.xcodeproj/xcshareddata/xcschemes/SpringApp.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 71 | 77 | 78 | 79 | 80 | 81 | 87 | 88 | 89 | 90 | 91 | 92 | 102 | 104 | 110 | 111 | 112 | 113 | 114 | 115 | 121 | 123 | 129 | 130 | 131 | 132 | 134 | 135 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /SpringApp/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SpringApp 4 | // 5 | // Created by Meng To on 2015-01-06. 6 | // Copyright (c) 2015 Meng To. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func applicationDidFinishLaunching(_ application: UIApplication) { 18 | // Override point for customization after application launch. 19 | } 20 | 21 | func applicationWillResignActive(_ application: UIApplication) { 22 | // 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. 23 | // 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. 24 | } 25 | 26 | func applicationDidEnterBackground(_ application: UIApplication) { 27 | // 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. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | func applicationWillEnterForeground(_ application: UIApplication) { 32 | // 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. 33 | } 34 | 35 | func applicationDidBecomeActive(_ application: UIApplication) { 36 | // 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. 37 | } 38 | 39 | func applicationWillTerminate(_ application: UIApplication) { 40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 41 | } 42 | 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /SpringApp/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /SpringApp/CodeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodeViewController.swift 3 | // DesignerNewsApp 4 | // 5 | // Created by Meng To on 2015-01-05. 6 | // Copyright (c) 2015 Meng To. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Spring 11 | 12 | class CodeViewController: UIViewController { 13 | 14 | @IBOutlet weak var modalView: SpringView! 15 | @IBOutlet weak var codeTextView: UITextView! 16 | @IBOutlet weak var titleLabel: UILabel! 17 | var codeText: String = "" 18 | var data: SpringView! 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | 23 | modalView.transform = CGAffineTransform(translationX: -300, y: 0) 24 | 25 | if data.animation != "" { 26 | codeText += "layer.animation = \"\(data.animation)\"\n" 27 | } 28 | if data.curve != "" { 29 | codeText += "layer.curve = \"\(data.curve)\"\n" 30 | } 31 | if data.force != 1 { 32 | codeText += String(format: "layer.force = %.1f\n", Double(data.force)) 33 | } 34 | if data.duration != 0.7 { 35 | codeText += String(format: "layer.duration = %.1f\n", Double(data.duration)) 36 | } 37 | if data.delay != 0 { 38 | codeText += String(format: "layer.delay = %.1f\n", Double(data.delay)) 39 | } 40 | if data.scaleX != 1 { 41 | codeText += String(format: "layer.scaleX = %.1f\n", Double(data.scaleX)) 42 | } 43 | if data.scaleY != 1 { 44 | codeText += String(format: "layer.scaleY = %.1f\n", Double(data.scaleY)) 45 | } 46 | if data.rotate != 0 { 47 | codeText += String(format: "layer.rotate = %.1f\n", Double(data.rotate)) 48 | } 49 | if data.damping != 0.7 { 50 | codeText += String(format: "layer.damping = %.1f\n", Double(data.damping)) 51 | } 52 | if data.velocity != 0.7 { 53 | codeText += String(format: "layer.velocity = %.1f\n", Double(data.velocity)) 54 | } 55 | codeText += "layer.animate()" 56 | 57 | codeTextView.text = codeText 58 | } 59 | 60 | @IBAction func closeButtonPressed(_ sender: AnyObject) { 61 | UIApplication.shared.sendAction(#selector(SpringViewController.maximizeView(_:)), to: nil, from: self, for: nil) 62 | 63 | modalView.animation = "slideRight" 64 | modalView.animateFrom = false 65 | modalView.animateToNext(completion: { 66 | self.dismiss(animated: false, completion: nil) 67 | }) 68 | } 69 | 70 | override func viewDidAppear(_ animated: Bool) { 71 | super.viewDidAppear(true) 72 | 73 | modalView.animate() 74 | 75 | UIApplication.shared.sendAction(#selector(SpringViewController.minimizeView(_:)), to: nil, from: self, for: nil) 76 | } 77 | } 78 | 79 | 80 | -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "appicon@58.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "appicon@87.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "40x40", 17 | "idiom" : "iphone", 18 | "filename" : "appicon@80.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "40x40", 23 | "idiom" : "iphone", 24 | "filename" : "appicon@120.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "60x60", 29 | "idiom" : "iphone", 30 | "filename" : "appicon@120-1.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "appicon@180.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "29x29", 41 | "idiom" : "ipad", 42 | "filename" : "appicon@29.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "29x29", 47 | "idiom" : "ipad", 48 | "filename" : "appicon@58-1.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "40x40", 53 | "idiom" : "ipad", 54 | "filename" : "appicon@40.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "40x40", 59 | "idiom" : "ipad", 60 | "filename" : "appicon@80-1.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "76x76", 65 | "idiom" : "ipad", 66 | "filename" : "appicon@76.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "76x76", 71 | "idiom" : "ipad", 72 | "filename" : "appicon@152.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "83.5x83.5", 77 | "idiom" : "ipad", 78 | "filename" : "appicon@167.png", 79 | "scale" : "2x" 80 | } 81 | ], 82 | "info" : { 83 | "version" : 1, 84 | "author" : "xcode" 85 | } 86 | } -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/AppIcon.appiconset/appicon@120-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Spring/50d92a5b9e08848387ae95bf44c6ad20834f7083/SpringApp/Images.xcassets/AppIcon.appiconset/appicon@120-1.png -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/AppIcon.appiconset/appicon@120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Spring/50d92a5b9e08848387ae95bf44c6ad20834f7083/SpringApp/Images.xcassets/AppIcon.appiconset/appicon@120.png -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/AppIcon.appiconset/appicon@152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Spring/50d92a5b9e08848387ae95bf44c6ad20834f7083/SpringApp/Images.xcassets/AppIcon.appiconset/appicon@152.png -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/AppIcon.appiconset/appicon@167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Spring/50d92a5b9e08848387ae95bf44c6ad20834f7083/SpringApp/Images.xcassets/AppIcon.appiconset/appicon@167.png -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/AppIcon.appiconset/appicon@180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Spring/50d92a5b9e08848387ae95bf44c6ad20834f7083/SpringApp/Images.xcassets/AppIcon.appiconset/appicon@180.png -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/AppIcon.appiconset/appicon@29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Spring/50d92a5b9e08848387ae95bf44c6ad20834f7083/SpringApp/Images.xcassets/AppIcon.appiconset/appicon@29.png -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/AppIcon.appiconset/appicon@40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Spring/50d92a5b9e08848387ae95bf44c6ad20834f7083/SpringApp/Images.xcassets/AppIcon.appiconset/appicon@40.png -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/AppIcon.appiconset/appicon@58-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Spring/50d92a5b9e08848387ae95bf44c6ad20834f7083/SpringApp/Images.xcassets/AppIcon.appiconset/appicon@58-1.png -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/AppIcon.appiconset/appicon@58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Spring/50d92a5b9e08848387ae95bf44c6ad20834f7083/SpringApp/Images.xcassets/AppIcon.appiconset/appicon@58.png -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/AppIcon.appiconset/appicon@76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Spring/50d92a5b9e08848387ae95bf44c6ad20834f7083/SpringApp/Images.xcassets/AppIcon.appiconset/appicon@76.png -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/AppIcon.appiconset/appicon@80-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Spring/50d92a5b9e08848387ae95bf44c6ad20834f7083/SpringApp/Images.xcassets/AppIcon.appiconset/appicon@80-1.png -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/AppIcon.appiconset/appicon@80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Spring/50d92a5b9e08848387ae95bf44c6ad20834f7083/SpringApp/Images.xcassets/AppIcon.appiconset/appicon@80.png -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/AppIcon.appiconset/appicon@87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Spring/50d92a5b9e08848387ae95bf44c6ad20834f7083/SpringApp/Images.xcassets/AppIcon.appiconset/appicon@87.png -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/icon-shape.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon-shape.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/icon-shape.imageset/icon-shape.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Spring/50d92a5b9e08848387ae95bf44c6ad20834f7083/SpringApp/Images.xcassets/icon-shape.imageset/icon-shape.pdf -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/loading.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "loading.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SpringApp/Images.xcassets/loading.imageset/loading.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Spring/50d92a5b9e08848387ae95bf44c6ad20834f7083/SpringApp/Images.xcassets/loading.imageset/loading.pdf -------------------------------------------------------------------------------- /SpringApp/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | Spring 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UIViewControllerBasedStatusBarAppearance 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /SpringApp/OptionsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OptionsViewController.swift 3 | // DesignerNewsApp 4 | // 5 | // Created by Meng To on 2015-01-04. 6 | // Copyright (c) 2015 Meng To. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Spring 11 | 12 | protocol OptionsViewControllerDelegate: class { 13 | func dampingSliderChanged(_ sender: AnyObject) 14 | func velocitySliderChanged(_ sender: AnyObject) 15 | func scaleSliderChanged(_ sender: AnyObject) 16 | func xSliderChanged(_ sender: AnyObject) 17 | func ySliderChanged(_ sender: AnyObject) 18 | func rotateSliderChanged(_ sender: AnyObject) 19 | func resetButtonPressed(_ sender: AnyObject) 20 | } 21 | 22 | class OptionsViewController: UIViewController { 23 | 24 | @IBOutlet weak var modalView: SpringView! 25 | 26 | @IBOutlet weak var dampingLabel: UILabel! 27 | @IBOutlet weak var velocityLabel: UILabel! 28 | @IBOutlet weak var scaleLabel: UILabel! 29 | @IBOutlet weak var xLabel: UILabel! 30 | @IBOutlet weak var yLabel: UILabel! 31 | @IBOutlet weak var rotateLabel: UILabel! 32 | 33 | @IBOutlet weak var dampingSlider: UISlider! 34 | @IBOutlet weak var velocitySlider: UISlider! 35 | @IBOutlet weak var scaleSlider: UISlider! 36 | @IBOutlet weak var xSlider: UISlider! 37 | @IBOutlet weak var ySlider: UISlider! 38 | @IBOutlet weak var rotateSlider: UISlider! 39 | 40 | var selectedDamping: CGFloat = 0.7 41 | var selectedVelocity: CGFloat = 0.7 42 | var selectedScale: CGFloat = 1 43 | var selectedX: CGFloat = 0 44 | var selectedY: CGFloat = 0 45 | var selectedRotate: CGFloat = 0 46 | 47 | weak var delegate: OptionsViewControllerDelegate? 48 | var data: SpringView! 49 | 50 | override func viewDidLoad() { 51 | super.viewDidLoad() 52 | 53 | modalView.transform = CGAffineTransform(translationX: 0, y: 300) 54 | 55 | dampingSlider.setValue(Float(data.damping), animated: true) 56 | velocitySlider.setValue(Float(data.velocity), animated: true) 57 | scaleSlider.setValue(Float(data.scaleX), animated: true) 58 | xSlider.setValue(Float(data.x), animated: true) 59 | ySlider.setValue(Float(data.y), animated: true) 60 | rotateSlider.setValue(Float(data.rotate), animated: true) 61 | 62 | dampingLabel.text = getString("Damping", value: data.damping) 63 | velocityLabel.text = getString("Velocity", value: data.velocity) 64 | scaleLabel.text = getString("Scale", value: data.scaleX) 65 | xLabel.text = getString("x", value: data.x) 66 | yLabel.text = getString("y", value: data.y) 67 | rotateLabel.text = getString("Rotate", value: data.rotate) 68 | } 69 | 70 | @IBAction func dampingSliderChanged(_ sender: AnyObject) { 71 | selectedDamping = sender.value(forKey: "value") as! CGFloat 72 | delegate?.dampingSliderChanged(sender) 73 | dampingLabel.text = getString("Damping", value: selectedDamping) 74 | } 75 | 76 | @IBAction func velocitySliderChanged(_ sender: AnyObject) { 77 | selectedVelocity = sender.value(forKey: "value") as! CGFloat 78 | delegate?.velocitySliderChanged(sender) 79 | velocityLabel.text = getString("Velocity", value: selectedVelocity) 80 | } 81 | 82 | @IBAction func scaleSliderChanged(_ sender: AnyObject) { 83 | selectedScale = sender.value(forKey: "value") as! CGFloat 84 | delegate?.scaleSliderChanged(sender) 85 | scaleLabel.text = getString("Scale", value: selectedScale) 86 | } 87 | 88 | @IBAction func xSliderChanged(_ sender: AnyObject) { 89 | selectedX = sender.value(forKey: "value") as! CGFloat 90 | delegate?.xSliderChanged(sender) 91 | xLabel.text = getString("X", value: selectedX) 92 | } 93 | 94 | @IBAction func ySliderChanged(_ sender: AnyObject) { 95 | selectedY = sender.value(forKey: "value") as! CGFloat 96 | delegate?.ySliderChanged(sender) 97 | yLabel.text = getString("Y", value: selectedY) 98 | } 99 | 100 | @IBAction func rotateSliderChanged(_ sender: AnyObject) { 101 | selectedRotate = sender.value(forKey: "value") as! CGFloat 102 | delegate?.rotateSliderChanged(sender) 103 | rotateLabel.text = getString("Rotate", value: selectedRotate) 104 | } 105 | 106 | @IBAction func resetButtonPressed(_ sender: AnyObject) { 107 | delegate?.resetButtonPressed(sender) 108 | dismiss(animated: true, completion: nil) 109 | 110 | UIApplication.shared.sendAction(#selector(SpringViewController.maximizeView(_:)), to: nil, from: self, for: nil) 111 | } 112 | 113 | @IBAction func closeButtonPressed(_ sender: AnyObject) { 114 | dismiss(animated: true, completion: nil) 115 | 116 | UIApplication.shared.sendAction(#selector(SpringViewController.maximizeView(_:)), to: nil, from: self, for: nil) 117 | } 118 | 119 | override func viewDidAppear(_ animated: Bool) { 120 | super.viewDidAppear(true) 121 | 122 | UIApplication.shared.sendAction(#selector(SpringViewController.minimizeView(_:)), to: nil, from: self, for: nil) 123 | 124 | modalView.animate() 125 | } 126 | 127 | func getString(_ name: String, value: CGFloat) -> String { 128 | return String(format: "\(name): %.1f", Double(value)) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /SpringApp/SpringViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpringViewController.swift 3 | // DesignerNewsApp 4 | // 5 | // Created by Meng To on 2015-01-02. 6 | // Copyright (c) 2015 Meng To. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Spring 11 | 12 | class SpringViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, OptionsViewControllerDelegate { 13 | 14 | @IBOutlet weak var delayLabel: UILabel! 15 | @IBOutlet weak var durationLabel: UILabel! 16 | @IBOutlet weak var forceLabel: UILabel! 17 | @IBOutlet weak var delaySlider: UISlider! 18 | @IBOutlet weak var durationSlider: UISlider! 19 | @IBOutlet weak var forceSlider: UISlider! 20 | @IBOutlet weak var ballView: SpringView! 21 | @IBOutlet weak var animationPicker: UIPickerView! 22 | 23 | var selectedRow: Int = 0 24 | var selectedEasing: Int = 0 25 | 26 | var selectedForce: CGFloat = 1 27 | var selectedDuration: CGFloat = 1 28 | var selectedDelay: CGFloat = 0 29 | 30 | var selectedDamping: CGFloat = 0.7 31 | var selectedVelocity: CGFloat = 0.7 32 | var selectedScale: CGFloat = 1 33 | var selectedX: CGFloat = 0 34 | var selectedY: CGFloat = 0 35 | var selectedRotate: CGFloat = 0 36 | 37 | @IBAction func forceSliderChanged(_ sender: AnyObject) { 38 | selectedForce = sender.value(forKey: "value") as! CGFloat 39 | animateView() 40 | forceLabel.text = String(format: "Force: %.1f", Double(selectedForce)) 41 | } 42 | @IBAction func durationSliderChanged(_ sender: AnyObject) { 43 | selectedDuration = sender.value(forKey: "value") as! CGFloat 44 | animateView() 45 | durationLabel.text = String(format: "Duration: %.1f", Double(selectedDuration)) 46 | } 47 | @IBAction func delaySliderChanged(_ sender: AnyObject) { 48 | selectedDelay = sender.value(forKey: "value") as! CGFloat 49 | animateView() 50 | delayLabel.text = String(format: "Delay: %.1f", Double(selectedDelay)) 51 | } 52 | 53 | func dampingSliderChanged(_ sender: AnyObject) { 54 | selectedDamping = sender.value(forKey: "value") as! CGFloat 55 | animateView() 56 | } 57 | 58 | func velocitySliderChanged(_ sender: AnyObject) { 59 | selectedVelocity = sender.value(forKey: "value") as! CGFloat 60 | animateView() 61 | } 62 | 63 | func scaleSliderChanged(_ sender: AnyObject) { 64 | selectedScale = sender.value(forKey: "value") as! CGFloat 65 | animateView() 66 | } 67 | 68 | func xSliderChanged(_ sender: AnyObject) { 69 | selectedX = sender.value(forKey: "value") as! CGFloat 70 | animateView() 71 | } 72 | 73 | func ySliderChanged(_ sender: AnyObject) { 74 | selectedY = sender.value(forKey: "value") as! CGFloat 75 | animateView() 76 | } 77 | 78 | func rotateSliderChanged(_ sender: AnyObject) { 79 | selectedRotate = sender.value(forKey: "value") as! CGFloat 80 | animateView() 81 | } 82 | 83 | func animateView() { 84 | setOptions() 85 | ballView.animate() 86 | } 87 | 88 | func setOptions() { 89 | ballView.force = selectedForce 90 | ballView.duration = selectedDuration 91 | ballView.delay = selectedDelay 92 | 93 | ballView.damping = selectedDamping 94 | ballView.velocity = selectedVelocity 95 | ballView.scaleX = selectedScale 96 | ballView.scaleY = selectedScale 97 | ballView.x = selectedX 98 | ballView.y = selectedY 99 | ballView.rotate = selectedRotate 100 | 101 | ballView.animation = animations[selectedRow].rawValue 102 | ballView.curve = animationCurves[selectedEasing].rawValue 103 | } 104 | 105 | @objc func minimizeView(_ sender: AnyObject) { 106 | SpringAnimation.spring(duration: 0.7, animations: { 107 | self.view.transform = CGAffineTransform(scaleX: 0.935, y: 0.935) 108 | }) 109 | UIApplication.shared.setStatusBarStyle(UIStatusBarStyle.lightContent, animated: true) 110 | } 111 | 112 | @objc func maximizeView(_ sender: AnyObject) { 113 | SpringAnimation.spring(duration: 0.7, animations: { 114 | self.view.transform = CGAffineTransform(scaleX: 1, y: 1) 115 | }) 116 | UIApplication.shared.setStatusBarStyle(UIStatusBarStyle.default, animated: true) 117 | } 118 | 119 | let animations: [Spring.AnimationPreset] = [ 120 | .Shake, 121 | .Pop, 122 | .Morph, 123 | .Squeeze, 124 | .Wobble, 125 | .Swing, 126 | .FlipX, 127 | .FlipY, 128 | .Fall, 129 | .SqueezeLeft, 130 | .SqueezeRight, 131 | .SqueezeDown, 132 | .SqueezeUp, 133 | .SlideLeft, 134 | .SlideRight, 135 | .SlideDown, 136 | .SlideUp, 137 | .FadeIn, 138 | .FadeOut, 139 | .FadeInLeft, 140 | .FadeInRight, 141 | .FadeInDown, 142 | .FadeInUp, 143 | .ZoomIn, 144 | .ZoomOut, 145 | .Flash 146 | ] 147 | 148 | var animationCurves: [Spring.AnimationCurve] = [ 149 | .EaseIn, 150 | .EaseOut, 151 | .EaseInOut, 152 | .Linear, 153 | .Spring, 154 | .EaseInSine, 155 | .EaseOutSine, 156 | .EaseInOutSine, 157 | .EaseInQuad, 158 | .EaseOutQuad, 159 | .EaseInOutQuad, 160 | .EaseInCubic, 161 | .EaseOutCubic, 162 | .EaseInOutCubic, 163 | .EaseInQuart, 164 | .EaseOutQuart, 165 | .EaseInOutQuart, 166 | .EaseInQuint, 167 | .EaseOutQuint, 168 | .EaseInOutQuint, 169 | .EaseInExpo, 170 | .EaseOutExpo, 171 | .EaseInOutExpo, 172 | .EaseInCirc, 173 | .EaseOutCirc, 174 | .EaseInOutCirc, 175 | .EaseInBack, 176 | .EaseOutBack, 177 | .EaseInOutBack 178 | ] 179 | 180 | override func viewDidLoad() { 181 | super.viewDidLoad() 182 | 183 | animationPicker.delegate = self 184 | animationPicker.dataSource = self 185 | animationPicker.showsSelectionIndicator = true 186 | } 187 | 188 | @IBAction func ballButtonPressed(_ sender: AnyObject) { 189 | 190 | UIView.animate(withDuration: 0.1, animations: { 191 | self.ballView.backgroundColor = UIColor(hex: "69DBFF") 192 | }, completion: { finished in 193 | UIView.animate(withDuration: 0.5, animations: { 194 | self.ballView.backgroundColor = UIColor(hex: "#279CEB") 195 | }) 196 | }) 197 | 198 | animateView() 199 | } 200 | 201 | var isBall = false 202 | func changeBall() { 203 | isBall = !isBall 204 | let animation = CABasicAnimation() 205 | let halfWidth = ballView.frame.width / 2 206 | let cornerRadius: CGFloat = isBall ? halfWidth : 10 207 | animation.keyPath = "cornerRadius" 208 | animation.fromValue = isBall ? 10 : halfWidth 209 | animation.toValue = cornerRadius 210 | animation.duration = 0.2 211 | ballView.layer.cornerRadius = cornerRadius 212 | ballView.layer.add(animation, forKey: "radius") 213 | } 214 | 215 | @IBAction func shapeButtonPressed(_ sender: AnyObject) { 216 | changeBall() 217 | } 218 | 219 | func resetButtonPressed(_ sender: AnyObject) { 220 | selectedForce = 1 221 | selectedDuration = 1 222 | selectedDelay = 0 223 | 224 | selectedDamping = 0.7 225 | selectedVelocity = 0.7 226 | selectedScale = 1 227 | selectedX = 0 228 | selectedY = 0 229 | selectedRotate = 0 230 | 231 | forceSlider.setValue(Float(selectedForce), animated: true) 232 | durationSlider.setValue(Float(selectedDuration), animated: true) 233 | delaySlider.setValue(Float(selectedDelay), animated: true) 234 | 235 | forceLabel.text = String(format: "Force: %.1f", Double(selectedForce)) 236 | durationLabel.text = String(format: "Duration: %.1f", Double(selectedDuration)) 237 | delayLabel.text = String(format: "Delay: %.1f", Double(selectedDelay)) 238 | } 239 | 240 | func numberOfComponents(in pickerView: UIPickerView) -> Int { 241 | return 2 242 | } 243 | 244 | func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 245 | return component == 0 ? animations.count : animationCurves.count 246 | } 247 | 248 | func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { 249 | return component == 0 ? animations[row].rawValue : animationCurves[row].rawValue 250 | } 251 | 252 | func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { 253 | switch component { 254 | case 0: 255 | selectedRow = row 256 | animateView() 257 | default: 258 | selectedEasing = row 259 | animateView() 260 | } 261 | } 262 | 263 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 264 | if let optionsViewController = segue.destination as? OptionsViewController { 265 | optionsViewController.delegate = self 266 | setOptions() 267 | optionsViewController.data = ballView 268 | } 269 | else if let codeViewController = segue.destination as? CodeViewController { 270 | setOptions() 271 | codeViewController.data = ballView 272 | } 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /SpringAppTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SpringAppTests/SpringAppTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpringAppTests.swift 3 | // SpringAppTests 4 | // 5 | // Created by Meng To on 2015-01-06. 6 | // Copyright (c) 2015 Meng To. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XCTest 11 | 12 | class SpringAppTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | XCTAssert(true, "Pass") 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure() { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /SpringTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SpringTests/SpringTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpringTests.swift 3 | // SpringTests 4 | // 5 | // Created by James Tang on 20/1/15. 6 | // Copyright (c) 2015 Meng To. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XCTest 11 | 12 | class SpringTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | XCTAssert(true, "Pass") 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure() { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | --------------------------------------------------------------------------------