Repository: MengTo/Spring Branch: master Commit: 50d92a5b9e08 Files: 53 Total size: 242.6 KB Directory structure: gitextract_6f9a0zjb/ ├── .gitignore ├── .swift-version ├── LICENSE ├── README.md ├── Spring/ │ ├── AsyncButton.swift │ ├── AsyncImageView.swift │ ├── AutoTextView.swift │ ├── BlurView.swift │ ├── DesignableButton.swift │ ├── DesignableImageView.swift │ ├── DesignableLabel.swift │ ├── DesignableTabBarController.swift │ ├── DesignableTextField.swift │ ├── DesignableTextView.swift │ ├── DesignableView.swift │ ├── ImageLoader.swift │ ├── Info.plist │ ├── KeyboardLayoutConstraint.swift │ ├── LoadingView.swift │ ├── LoadingView.xib │ ├── Misc.swift │ ├── SoundPlayer.swift │ ├── Spring.h │ ├── Spring.swift │ ├── SpringAnimation.swift │ ├── SpringButton.swift │ ├── SpringImageView.swift │ ├── SpringLabel.swift │ ├── SpringTextField.swift │ ├── SpringTextView.swift │ ├── SpringView.swift │ ├── TransitionManager.swift │ ├── TransitionZoom.swift │ └── UnwindSegue.swift ├── Spring.podspec ├── SpringApp/ │ ├── AppDelegate.swift │ ├── Base.lproj/ │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── CodeViewController.swift │ ├── Images.xcassets/ │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── icon-shape.imageset/ │ │ │ └── Contents.json │ │ └── loading.imageset/ │ │ └── Contents.json │ ├── Info.plist │ ├── OptionsViewController.swift │ └── SpringViewController.swift ├── SpringApp.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ └── contents.xcworkspacedata │ └── xcshareddata/ │ └── xcschemes/ │ ├── Spring.xcscheme │ └── SpringApp.xcscheme ├── SpringAppTests/ │ ├── Info.plist │ └── SpringAppTests.swift └── SpringTests/ ├── Info.plist └── SpringTests.swift ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ build/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata *.xccheckout *.moved-aside DerivedData *.hmap *.ipa *.xcuserstate .DS_Store ================================================ FILE: .swift-version ================================================ 4 ================================================ FILE: LICENSE ================================================ Copyright (c) 2014-2015 Meng To (meng@designcode.io) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ ## Updated for Swift 4.2 Requires Xcode 10 and Swift 4.2. ## Installation Drop in the Spring folder to your Xcode project (make sure to enable "Copy items if needed" and "Create groups"). Or via CocoaPods: ``` use_frameworks! pod 'Spring', :git => 'https://github.com/MengTo/Spring.git' ``` ## Usage with Storyboard In Identity Inspector, connect the UIView to SpringView Class and set the animation properties in Attribute Inspector. ![](http://cl.ly/image/241o0G1G3S36/download/springsetup.jpg) ## Usage with Code layer.animation = "squeezeDown" layer.animate() ## Demo The Animations ![](http://cl.ly/image/1n1E2j3W3y24/springscreen.jpg) ## Chaining Animations layer.y = -50 animateToNext { layer.animation = "fall" layer.animateTo() } ## Functions animate() animateNext { ... } animateTo() animateToNext { ... } ## Animation shake pop morph squeeze wobble swing flipX flipY fall squeezeLeft squeezeRight squeezeDown squeezeUp slideLeft slideRight slideDown slideUp fadeIn fadeOut fadeInLeft fadeInRight fadeInDown fadeInUp zoomIn zoomOut flash ## Curve spring linear easeIn easeOut easeInOut ## Properties force duration delay damping velocity repeatCount scale x y rotate \* Not all properties work together. Play with the demo app. ## Autostart Allows you to animate without code. Don't need to use this if you plan to start the animation in code. ## Autohide Saves you the hassle of adding a line "layer.alpha = 0" in viewDidLoad(). ## Known issue Animations won't autostart when view is reached via performSegueWithIdentifier. ## Tutorials - Tutorials available on [Design+Code](https://designcode.io/swiftapp). - [Integrate Spring to existing Objective-C projects](https://medium.com/ios-apprentice/using-swift-in-objective-c-projects-f7e7a09f8be) ## ChangeLog - At [ChangeLog](https://github.com/MengTo/Spring/wiki/CHANGELOG) wiki page ## License Spring is released under the MIT license. See LICENSE for details. ================================================ FILE: Spring/AsyncButton.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 James Tang (j@jamztang.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit public class AsyncButton: UIButton { private var imageURL = [UInt:NSURL]() private var placeholderImage = [UInt:UIImage]() public func setImageURL(url: NSURL?, placeholderImage placeholder:UIImage?, forState state:UIControl.State) { imageURL[state.rawValue] = url placeholderImage[state.rawValue] = placeholder if let urlString = url?.absoluteString { ImageLoader.sharedLoader.imageForUrl(urlString: urlString) { [weak self] image, url in if let strongSelf = self { DispatchQueue.main.async(execute: { () -> Void in if strongSelf.imageURL[state.rawValue]?.absoluteString == url { strongSelf.setImage(image, for: state) } }) } } } } } ================================================ FILE: Spring/AsyncImageView.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 James Tang (j@jamztang.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit public class AsyncImageView: UIImageView { public var placeholderImage : UIImage? public var url : NSURL? { didSet { self.image = placeholderImage if let urlString = url?.absoluteString { ImageLoader.sharedLoader.imageForUrl(urlString: urlString) { [weak self] image, url in if let strongSelf = self { DispatchQueue.main.async(execute: { () -> Void in if strongSelf.url?.absoluteString == url { strongSelf.image = image ?? strongSelf.placeholderImage } }) } } } } } public func setURL(url: NSURL?, placeholderImage: UIImage?) { self.placeholderImage = placeholderImage self.url = url } } ================================================ FILE: Spring/AutoTextView.swift ================================================ // // AutoTextView.swift // SpringApp // // Created by Meng To on 2015-03-27. // Copyright (c) 2015 Meng To. All rights reserved. // import UIKit public class AutoTextView: UITextView { public override var intrinsicContentSize: CGSize { get { var size = self.sizeThatFits(CGSize(width: self.frame.size.width, height: CGFloat.greatestFiniteMagnitude)) size.width = self.frame.size.width if text.length == 0 { size.height = 0 } contentInset = UIEdgeInsets(top: -4, left: -4, bottom: -4, right: -4) layoutIfNeeded() return size } } } ================================================ FILE: Spring/BlurView.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit public func insertBlurView (view: UIView, style: UIBlurEffect.Style) -> UIVisualEffectView { view.backgroundColor = UIColor.clear let blurEffect = UIBlurEffect(style: style) let blurEffectView = UIVisualEffectView(effect: blurEffect) blurEffectView.frame = view.bounds view.insertSubview(blurEffectView, at: 0) return blurEffectView } ================================================ FILE: Spring/DesignableButton.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit @IBDesignable public class DesignableButton: SpringButton { @IBInspectable public var borderColor: UIColor = UIColor.clear { didSet { layer.borderColor = borderColor.cgColor } } @IBInspectable public var borderWidth: CGFloat = 0 { didSet { layer.borderWidth = borderWidth } } @IBInspectable public var cornerRadius: CGFloat = 0 { didSet { layer.cornerRadius = cornerRadius } } @IBInspectable public var shadowColor: UIColor = UIColor.clear { didSet { layer.shadowColor = shadowColor.cgColor } } @IBInspectable public var shadowRadius: CGFloat = 0 { didSet { layer.shadowRadius = shadowRadius } } @IBInspectable public var shadowOpacity: CGFloat = 0 { didSet { layer.shadowOpacity = Float(shadowOpacity) } } @IBInspectable public var shadowOffsetY: CGFloat = 0 { didSet { layer.shadowOffset.height = shadowOffsetY } } } ================================================ FILE: Spring/DesignableImageView.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit @IBDesignable public class DesignableImageView: SpringImageView { @IBInspectable public var borderColor: UIColor = UIColor.clear { didSet { layer.borderColor = borderColor.cgColor } } @IBInspectable public var borderWidth: CGFloat = 0 { didSet { layer.borderWidth = borderWidth } } @IBInspectable public var cornerRadius: CGFloat = 0 { didSet { layer.cornerRadius = cornerRadius } } } ================================================ FILE: Spring/DesignableLabel.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit @IBDesignable public class DesignableLabel: SpringLabel { @IBInspectable public var lineHeight: CGFloat = 1.5 { didSet { let font = UIFont(name: self.font.fontName, size: self.font.pointSize) guard let text = self.text else { return } let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = lineHeight let attributedString = NSMutableAttributedString(string: text) attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, attributedString.length)) attributedString.addAttribute(NSAttributedString.Key.font, value: font!, range: NSMakeRange(0, attributedString.length)) self.attributedText = attributedString } } } ================================================ FILE: Spring/DesignableTabBarController.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit @IBDesignable class DesignableTabBarController: UITabBarController { @IBInspectable var normalTint: UIColor = UIColor.clear { didSet { UITabBar.appearance().tintColor = normalTint UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: normalTint], for: UIControl.State()) } } @IBInspectable var selectedTint: UIColor = UIColor.clear { didSet { UITabBar.appearance().tintColor = selectedTint UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: selectedTint], for:UIControl.State.selected) } } @IBInspectable var fontName: String = "" { didSet { UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: normalTint, NSAttributedString.Key.font: UIFont(name: fontName, size: 11)!], for: UIControl.State()) } } @IBInspectable var firstSelectedImage: UIImage? { didSet { if let image = firstSelectedImage { var tabBarItems = self.tabBar.items as [UITabBarItem]? tabBarItems?[0].selectedImage = image.withRenderingMode(UIImage.RenderingMode.alwaysTemplate) } } } @IBInspectable var secondSelectedImage: UIImage? { didSet { if let image = secondSelectedImage { var tabBarItems = self.tabBar.items as [UITabBarItem]? tabBarItems?[1].selectedImage = image.withRenderingMode(UIImage.RenderingMode.alwaysTemplate) } } } @IBInspectable var thirdSelectedImage: UIImage? { didSet { if let image = thirdSelectedImage { var tabBarItems = self.tabBar.items as [UITabBarItem]? tabBarItems?[2].selectedImage = image.withRenderingMode(UIImage.RenderingMode.alwaysTemplate) } } } @IBInspectable var fourthSelectedImage: UIImage? { didSet { if let image = fourthSelectedImage { var tabBarItems = self.tabBar.items as [UITabBarItem]? tabBarItems?[3].selectedImage = image.withRenderingMode(UIImage.RenderingMode.alwaysTemplate) } } } @IBInspectable var fifthSelectedImage: UIImage? { didSet { if let image = fifthSelectedImage { var tabBarItems = self.tabBar.items as [UITabBarItem]? tabBarItems?[4].selectedImage = image.withRenderingMode(UIImage.RenderingMode.alwaysTemplate) } } } override func viewDidLoad() { super.viewDidLoad() if let items = self.tabBar.items { for item in items { if let image = item.image { item.image = image.imageWithColor(tintColor: self.normalTint).withRenderingMode(UIImage.RenderingMode.alwaysOriginal) } } } } } extension UIImage { func imageWithColor(tintColor: UIColor) -> UIImage { UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale) let context = UIGraphicsGetCurrentContext() context!.translateBy(x: 0, y: self.size.height) context!.scaleBy(x: 1.0, y: -1.0); context!.setBlendMode(CGBlendMode.normal) let rect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height) context?.clip(to: rect, mask: self.cgImage!) tintColor.setFill() context!.fill(rect) let newImage = UIGraphicsGetImageFromCurrentImageContext()! as UIImage UIGraphicsEndImageContext() return newImage } } ================================================ FILE: Spring/DesignableTextField.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit @IBDesignable public class DesignableTextField: SpringTextField { @IBInspectable public var placeholderColor: UIColor = UIColor.clear { didSet { guard let placeholder = placeholder else { return } attributedPlaceholder = NSAttributedString(string: placeholder, attributes: [NSAttributedString.Key.foregroundColor: placeholderColor]) layoutSubviews() } } @IBInspectable public var sidePadding: CGFloat = 0 { didSet { let padding = UIView(frame: CGRect(x: 0, y: 0, width: sidePadding, height: sidePadding)) leftViewMode = UITextField.ViewMode.always leftView = padding rightViewMode = UITextField.ViewMode.always rightView = padding } } @IBInspectable public var leftPadding: CGFloat = 0 { didSet { let padding = UIView(frame: CGRect(x: 0, y: 0, width: leftPadding, height: 0)) leftViewMode = UITextField.ViewMode.always leftView = padding } } @IBInspectable public var rightPadding: CGFloat = 0 { didSet { let padding = UIView(frame: CGRect(x: 0, y: 0, width: rightPadding, height: 0)) rightViewMode = UITextField.ViewMode.always rightView = padding } } @IBInspectable public var borderColor: UIColor = UIColor.clear { didSet { layer.borderColor = borderColor.cgColor } } @IBInspectable public var borderWidth: CGFloat = 0 { didSet { layer.borderWidth = borderWidth } } @IBInspectable public var cornerRadius: CGFloat = 0 { didSet { layer.cornerRadius = cornerRadius } } @IBInspectable public var lineHeight: CGFloat = 1.5 { didSet { let font = UIFont(name: self.font!.fontName, size: self.font!.pointSize) guard let text = self.text else { return } let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = lineHeight let attributedString = NSMutableAttributedString(string: text) attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length)) attributedString.addAttribute(NSAttributedString.Key.font, value: font!, range: NSRange(location: 0, length: attributedString.length)) self.attributedText = attributedString } } } ================================================ FILE: Spring/DesignableTextView.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit @IBDesignable public class DesignableTextView: SpringTextView { @IBInspectable public var borderColor: UIColor = UIColor.clear { didSet { layer.borderColor = borderColor.cgColor } } @IBInspectable public var borderWidth: CGFloat = 0 { didSet { layer.borderWidth = borderWidth } } @IBInspectable public var cornerRadius: CGFloat = 0 { didSet { layer.cornerRadius = cornerRadius } } @IBInspectable public var lineHeight: CGFloat = 1.5 { didSet { let font = UIFont(name: self.font!.fontName, size: self.font!.pointSize) guard let text = self.text else { return } let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = lineHeight let attributedString = NSMutableAttributedString(string: text) attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length)) attributedString.addAttribute(NSAttributedString.Key.font, value: font!, range: NSRange(location: 0, length: attributedString.length)) self.attributedText = attributedString } } } ================================================ FILE: Spring/DesignableView.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit @IBDesignable public class DesignableView: SpringView { @IBInspectable public var borderColor: UIColor = UIColor.clear { didSet { layer.borderColor = borderColor.cgColor } } @IBInspectable public var borderWidth: CGFloat = 0 { didSet { layer.borderWidth = borderWidth } } @IBInspectable public var cornerRadius: CGFloat = 0 { didSet { layer.cornerRadius = cornerRadius } } @IBInspectable public var shadowColor: UIColor = UIColor.clear { didSet { layer.shadowColor = shadowColor.cgColor } } @IBInspectable public var shadowRadius: CGFloat = 0 { didSet { layer.shadowRadius = shadowRadius } } @IBInspectable public var shadowOpacity: CGFloat = 0 { didSet { layer.shadowOpacity = Float(shadowOpacity) } } @IBInspectable public var shadowOffsetY: CGFloat = 0 { didSet { layer.shadowOffset.height = shadowOffsetY } } } ================================================ FILE: Spring/ImageLoader.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2014 Nate Lyman (https://github.com/natelyman/SwiftImageLoader) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit import Foundation public class ImageLoader { var cache = NSCache() public class var sharedLoader : ImageLoader { struct Static { static let instance : ImageLoader = ImageLoader() } return Static.instance } public func imageForUrl(urlString: String, completionHandler: @escaping(_ image: UIImage?, _ url: String) -> ()) { DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async { var data: NSData? if let dataCache = self.cache.object(forKey: urlString as NSString){ data = (dataCache) as NSData }else{ if (URL(string: urlString) != nil) { data = NSData(contentsOf: URL(string: urlString)!) if data != nil { self.cache.setObject(data!, forKey: urlString as NSString) } }else{ return } } if let goodData = data { let image = UIImage(data: goodData as Data) DispatchQueue.main.async(execute: {() in completionHandler(image, urlString) }) return } let downloadTask: URLSessionDataTask = URLSession.shared.dataTask(with: URL(string: urlString)!, completionHandler: { (data, response, error) -> Void in if (error != nil) { completionHandler(nil, urlString) return } if data != nil { let image = UIImage(data: data!) self.cache.setObject(data! as NSData, forKey: urlString as NSString) DispatchQueue.main.async(execute: {() in completionHandler(image, urlString) }) return } }) downloadTask.resume() } } } ================================================ FILE: Spring/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass ================================================ FILE: Spring/KeyboardLayoutConstraint.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 James Tang (j@jamztang.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit #if !os(tvOS) @available(tvOS, unavailable) public class KeyboardLayoutConstraint: NSLayoutConstraint { private var offset : CGFloat = 0 private var keyboardVisibleHeight : CGFloat = 0 @available(tvOS, unavailable) override public func awakeFromNib() { super.awakeFromNib() offset = constant NotificationCenter.default.addObserver(self, selector: #selector(KeyboardLayoutConstraint.keyboardWillShowNotification(_:)), name: UIWindow.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(KeyboardLayoutConstraint.keyboardWillHideNotification(_:)), name: UIWindow.keyboardWillHideNotification, object: nil) } deinit { NotificationCenter.default.removeObserver(self) } // MARK: Notification @objc func keyboardWillShowNotification(_ notification: Notification) { if let userInfo = notification.userInfo { if let frameValue = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue { let frame = frameValue.cgRectValue keyboardVisibleHeight = frame.size.height } self.updateConstant() switch (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber, userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber) { case let (.some(duration), .some(curve)): let options = UIView.AnimationOptions(rawValue: curve.uintValue) UIView.animate( withDuration: TimeInterval(duration.doubleValue), delay: 0, options: options, animations: { UIApplication.shared.keyWindow?.layoutIfNeeded() return }, completion: { finished in }) default: break } } } @objc func keyboardWillHideNotification(_ notification: NSNotification) { keyboardVisibleHeight = 0 self.updateConstant() if let userInfo = notification.userInfo { switch (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber, userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber) { case let (.some(duration), .some(curve)): let options = UIView.AnimationOptions(rawValue: curve.uintValue) UIView.animate( withDuration: TimeInterval(duration.doubleValue), delay: 0, options: options, animations: { UIApplication.shared.keyWindow?.layoutIfNeeded() return }, completion: { finished in }) default: break } } } func updateConstant() { self.constant = offset + keyboardVisibleHeight } } #endif ================================================ FILE: Spring/LoadingView.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit #if !os(tvOS) @available(tvOS, unavailable) public class LoadingView: UIView { @IBOutlet public weak var indicatorView: SpringView! override public func awakeFromNib() { let animation = CABasicAnimation() animation.keyPath = "transform.rotation.z" animation.fromValue = degreesToRadians(degrees: 0) animation.toValue = degreesToRadians(degrees: 360) animation.duration = 0.9 animation.repeatCount = HUGE indicatorView.layer.add(animation, forKey: "") } class func designCodeLoadingView() -> UIView { return Bundle(for: self).loadNibNamed("LoadingView", owner: self, options: nil)![0] as! UIView } } public extension UIView { struct LoadingViewConstants { static let Tag = 1000 } public func showLoading() { if self.viewWithTag(LoadingViewConstants.Tag) != nil { // If loading view is already found in current view hierachy, do nothing return } let loadingXibView = LoadingView.designCodeLoadingView() loadingXibView.frame = self.bounds loadingXibView.tag = LoadingViewConstants.Tag self.addSubview(loadingXibView) loadingXibView.alpha = 0 SpringAnimation.spring(duration: 0.7, animations: { loadingXibView.alpha = 1 }) } public func hideLoading() { if let loadingXibView = self.viewWithTag(LoadingViewConstants.Tag) { loadingXibView.alpha = 1 SpringAnimation.springWithCompletion(duration: 0.7, animations: { loadingXibView.alpha = 0 loadingXibView.transform = CGAffineTransform(scaleX: 3, y: 3) }, completion: { (completed) -> Void in loadingXibView.removeFromSuperview() }) } } } #endif ================================================ FILE: Spring/LoadingView.xib ================================================ ================================================ FILE: Spring/Misc.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit public extension String { var length: Int { return self.count } func toURL() -> NSURL? { return NSURL(string: self) } } public func htmlToAttributedString(text: String) -> NSAttributedString! { guard let htmlData = text.data(using: String.Encoding.utf8, allowLossyConversion: false) else { return NSAttributedString() } let htmlString: NSAttributedString? do { htmlString = try NSAttributedString(data: htmlData, options: [NSAttributedString.DocumentReadingOptionKey.documentType:NSAttributedString.DocumentType.html], documentAttributes: nil) } catch _ { htmlString = nil } return htmlString } public func degreesToRadians(degrees: CGFloat) -> CGFloat { return degrees * CGFloat(CGFloat.pi / 180) } public func delay(delay:Double, closure: @escaping ()->()) { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure) } public func imageFromURL(_ Url: String) -> UIImage { let url = Foundation.URL(string: Url) let data = try? Data(contentsOf: url!) return UIImage(data: data!)! } public extension UIColor { convenience init(hex: String) { var red: CGFloat = 0.0 var green: CGFloat = 0.0 var blue: CGFloat = 0.0 var alpha: CGFloat = 1.0 var hex: String = hex if hex.hasPrefix("#") { let index = hex.index(hex.startIndex, offsetBy: 1) hex = String(hex[index...]) } let scanner = Scanner(string: hex) var hexValue: CUnsignedLongLong = 0 if scanner.scanHexInt64(&hexValue) { switch (hex.count) { case 3: red = CGFloat((hexValue & 0xF00) >> 8) / 15.0 green = CGFloat((hexValue & 0x0F0) >> 4) / 15.0 blue = CGFloat(hexValue & 0x00F) / 15.0 case 4: red = CGFloat((hexValue & 0xF000) >> 12) / 15.0 green = CGFloat((hexValue & 0x0F00) >> 8) / 15.0 blue = CGFloat((hexValue & 0x00F0) >> 4) / 15.0 alpha = CGFloat(hexValue & 0x000F) / 15.0 case 6: red = CGFloat((hexValue & 0xFF0000) >> 16) / 255.0 green = CGFloat((hexValue & 0x00FF00) >> 8) / 255.0 blue = CGFloat(hexValue & 0x0000FF) / 255.0 case 8: red = CGFloat((hexValue & 0xFF000000) >> 24) / 255.0 green = CGFloat((hexValue & 0x00FF0000) >> 16) / 255.0 blue = CGFloat((hexValue & 0x0000FF00) >> 8) / 255.0 alpha = CGFloat(hexValue & 0x000000FF) / 255.0 default: print("Invalid RGB string, number of characters after '#' should be either 3, 4, 6 or 8", terminator: "") } } else { print("Scan hex error") } self.init(red:red, green:green, blue:blue, alpha:alpha) } } public func rgbaToUIColor(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) -> UIColor { return UIColor(red: red, green: green, blue: blue, alpha: alpha) } public func UIColorFromRGB(rgbValue: UInt) -> UIColor { return UIColor( red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, blue: CGFloat(rgbValue & 0x0000FF) / 255.0, alpha: CGFloat(1.0) ) } public func stringFromDate(date: NSDate, format: String) -> String { let dateFormatter = DateFormatter() dateFormatter.dateFormat = format return dateFormatter.string(from: date as Date) } public func dateFromString(date: String, format: String) -> Date { let dateFormatter = DateFormatter() dateFormatter.dateFormat = format if let date = dateFormatter.date(from: date) { return date } else { return Date(timeIntervalSince1970: 0) } } public func randomStringWithLength (len : Int) -> NSString { let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" let randomString : NSMutableString = NSMutableString(capacity: len) for _ in 0 ..< len { let length = UInt32 (letters.length) let rand = arc4random_uniform(length) randomString.appendFormat("%C", letters.character(at: Int(rand))) } return randomString } public func timeAgoSinceDate(date: Date, numericDates: Bool) -> String { let calendar = Calendar.current let unitFlags = Set(arrayLiteral: Calendar.Component.minute, Calendar.Component.hour, Calendar.Component.day, Calendar.Component.weekOfYear, Calendar.Component.month, Calendar.Component.year, Calendar.Component.second) let now = Date() let dateComparison = now.compare(date) var earliest: Date var latest: Date switch dateComparison { case .orderedAscending: earliest = now latest = date default: earliest = date latest = now } let components: DateComponents = calendar.dateComponents(unitFlags, from: earliest, to: latest) guard let year = components.year, let month = components.month, let weekOfYear = components.weekOfYear, let day = components.day, let hour = components.hour, let minute = components.minute, let second = components.second else { fatalError() } if (year >= 2) { return "\(year)y" } else if (year >= 1) { if (numericDates){ return "1y" } else { return "1y" } } else if (month >= 2) { return "\(month * 4)w" } else if (month >= 1) { if (numericDates){ return "4w" } else { return "4w" } } else if (weekOfYear >= 2) { return "\(weekOfYear)w" } else if (weekOfYear >= 1){ if (numericDates){ return "1w" } else { return "1w" } } else if (day >= 2) { return "\(components.day ?? 2)d" } else if (day >= 1){ if (numericDates){ return "1d" } else { return "1d" } } else if (hour >= 2) { return "\(hour)h" } else if (hour >= 1){ if (numericDates){ return "1h" } else { return "1h" } } else if (minute >= 2) { return "\(minute)m" } else if (minute >= 1){ if (numericDates){ return "1m" } else { return "1m" } } else if (second >= 3) { return "\(second)s" } else { return "now" } } extension UIImageView { func setImage(url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit, placeholderImage: UIImage?) { contentMode = mode URLSession.shared.dataTask(with: url) { (data, response, error) in guard let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200, let mimeType = response?.mimeType, mimeType.hasPrefix("image"), let data = data, error == nil, let image = UIImage(data: data) else { self.image = placeholderImage return } DispatchQueue.main.async() { () -> Void in self.image = image } }.resume() } func setImage(urlString: String, contentMode mode: UIView.ContentMode = .scaleAspectFit, placeholderImage: UIImage?) { guard let url = URL(string: urlString) else { image = placeholderImage return } setImage(url: url, contentMode: mode, placeholderImage: placeholderImage) } } ================================================ FILE: Spring/SoundPlayer.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 James Tang (j@jamztang.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit import AudioToolbox public struct SoundPlayer { static var filename : String? static var enabled : Bool = true private struct Internal { static var cache = [URL:SystemSoundID]() } public static func playSound(soundFile: String) { if !enabled { return } if let url = Bundle.main.url(forResource: soundFile, withExtension: nil) { var soundID : SystemSoundID = Internal.cache[url] ?? 0 if soundID == 0 { AudioServicesCreateSystemSoundID(url as CFURL, &soundID) Internal.cache[url] = soundID } AudioServicesPlaySystemSound(soundID) } else { print("Could not find sound file name `\(soundFile)`") } } static func play(file: String) { self.playSound(soundFile: file) } } ================================================ FILE: Spring/Spring.h ================================================ // // Spring.h // Spring // // Created by James Tang on 20/1/15. // Copyright (c) 2015 Meng To. All rights reserved. // #import //! Project version number for Spring. FOUNDATION_EXPORT double SpringVersionNumber; //! Project version string for Spring. FOUNDATION_EXPORT const unsigned char SpringVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import ================================================ FILE: Spring/Spring.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit @objc public protocol Springable { var autostart: Bool { get set } var autohide: Bool { get set } var animation: String { get set } var force: CGFloat { get set } var delay: CGFloat { get set } var duration: CGFloat { get set } var damping: CGFloat { get set } var velocity: CGFloat { get set } var repeatCount: Float { get set } var x: CGFloat { get set } var y: CGFloat { get set } var scaleX: CGFloat { get set } var scaleY: CGFloat { get set } var rotate: CGFloat { get set } var opacity: CGFloat { get set } var animateFrom: Bool { get set } var curve: String { get set } // UIView var layer : CALayer { get } var transform : CGAffineTransform { get set } var alpha : CGFloat { get set } func animate() func animateNext(completion: @escaping () -> ()) func animateTo() func animateToNext(completion: @escaping () -> ()) } public class Spring : NSObject { private unowned var view : Springable private var shouldAnimateAfterActive = false private var shouldAnimateInLayoutSubviews = true init(_ view: Springable) { self.view = view super.init() commonInit() } func commonInit() { NotificationCenter.default.addObserver(self, selector: #selector(Spring.didBecomeActiveNotification(_:)), name: UIApplication.didBecomeActiveNotification, object: nil) } @objc func didBecomeActiveNotification(_ notification: NSNotification) { if shouldAnimateAfterActive { alpha = 0 animate() shouldAnimateAfterActive = false } } deinit { NotificationCenter.default.removeObserver(self, name: UIApplication.didBecomeActiveNotification, object: nil) } private var autostart: Bool { set { self.view.autostart = newValue } get { return self.view.autostart }} private var autohide: Bool { set { self.view.autohide = newValue } get { return self.view.autohide }} private var animation: String { set { self.view.animation = newValue } get { return self.view.animation }} private var force: CGFloat { set { self.view.force = newValue } get { return self.view.force }} private var delay: CGFloat { set { self.view.delay = newValue } get { return self.view.delay }} private var duration: CGFloat { set { self.view.duration = newValue } get { return self.view.duration }} private var damping: CGFloat { set { self.view.damping = newValue } get { return self.view.damping }} private var velocity: CGFloat { set { self.view.velocity = newValue } get { return self.view.velocity }} private var repeatCount: Float { set { self.view.repeatCount = newValue } get { return self.view.repeatCount }} private var x: CGFloat { set { self.view.x = newValue } get { return self.view.x }} private var y: CGFloat { set { self.view.y = newValue } get { return self.view.y }} private var scaleX: CGFloat { set { self.view.scaleX = newValue } get { return self.view.scaleX }} private var scaleY: CGFloat { set { self.view.scaleY = newValue } get { return self.view.scaleY }} private var rotate: CGFloat { set { self.view.rotate = newValue } get { return self.view.rotate }} private var opacity: CGFloat { set { self.view.opacity = newValue } get { return self.view.opacity }} private var animateFrom: Bool { set { self.view.animateFrom = newValue } get { return self.view.animateFrom }} private var curve: String { set { self.view.curve = newValue } get { return self.view.curve }} // UIView private var layer : CALayer { return view.layer } private var transform : CGAffineTransform { get { return view.transform } set { view.transform = newValue }} private var alpha: CGFloat { get { return view.alpha } set { view.alpha = newValue } } public enum AnimationPreset: String { case SlideLeft = "slideLeft" case SlideRight = "slideRight" case SlideDown = "slideDown" case SlideUp = "slideUp" case SqueezeLeft = "squeezeLeft" case SqueezeRight = "squeezeRight" case SqueezeDown = "squeezeDown" case SqueezeUp = "squeezeUp" case FadeIn = "fadeIn" case FadeOut = "fadeOut" case FadeOutIn = "fadeOutIn" case FadeInLeft = "fadeInLeft" case FadeInRight = "fadeInRight" case FadeInDown = "fadeInDown" case FadeInUp = "fadeInUp" case ZoomIn = "zoomIn" case ZoomOut = "zoomOut" case Fall = "fall" case Shake = "shake" case Pop = "pop" case FlipX = "flipX" case FlipY = "flipY" case Morph = "morph" case Squeeze = "squeeze" case Flash = "flash" case Wobble = "wobble" case Swing = "swing" } public enum AnimationCurve: String { case EaseIn = "easeIn" case EaseOut = "easeOut" case EaseInOut = "easeInOut" case Linear = "linear" case Spring = "spring" case EaseInSine = "easeInSine" case EaseOutSine = "easeOutSine" case EaseInOutSine = "easeInOutSine" case EaseInQuad = "easeInQuad" case EaseOutQuad = "easeOutQuad" case EaseInOutQuad = "easeInOutQuad" case EaseInCubic = "easeInCubic" case EaseOutCubic = "easeOutCubic" case EaseInOutCubic = "easeInOutCubic" case EaseInQuart = "easeInQuart" case EaseOutQuart = "easeOutQuart" case EaseInOutQuart = "easeInOutQuart" case EaseInQuint = "easeInQuint" case EaseOutQuint = "easeOutQuint" case EaseInOutQuint = "easeInOutQuint" case EaseInExpo = "easeInExpo" case EaseOutExpo = "easeOutExpo" case EaseInOutExpo = "easeInOutExpo" case EaseInCirc = "easeInCirc" case EaseOutCirc = "easeOutCirc" case EaseInOutCirc = "easeInOutCirc" case EaseInBack = "easeInBack" case EaseOutBack = "easeOutBack" case EaseInOutBack = "easeInOutBack" } func animatePreset() { alpha = 0.99 if let animation = AnimationPreset(rawValue: animation) { switch animation { case .SlideLeft: x = 300*force case .SlideRight: x = -300*force case .SlideDown: y = -300*force case .SlideUp: y = 300*force case .SqueezeLeft: x = 300 scaleX = 3*force case .SqueezeRight: x = -300 scaleX = 3*force case .SqueezeDown: y = -300 scaleY = 3*force case .SqueezeUp: y = 300 scaleY = 3*force case .FadeIn: opacity = 0 case .FadeOut: animateFrom = false opacity = 0 case .FadeOutIn: let animation = CABasicAnimation() animation.keyPath = "opacity" animation.fromValue = 1 animation.toValue = 0 animation.timingFunction = getTimingFunction(curve: curve) animation.duration = CFTimeInterval(duration) animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) animation.autoreverses = true layer.add(animation, forKey: "fade") case .FadeInLeft: opacity = 0 x = 300*force case .FadeInRight: x = -300*force opacity = 0 case .FadeInDown: y = -300*force opacity = 0 case .FadeInUp: y = 300*force opacity = 0 case .ZoomIn: opacity = 0 scaleX = 2*force scaleY = 2*force case .ZoomOut: animateFrom = false opacity = 0 scaleX = 2*force scaleY = 2*force case .Fall: animateFrom = false rotate = 15 * CGFloat(CGFloat.pi/180) y = 600*force case .Shake: let animation = CAKeyframeAnimation() animation.keyPath = "position.x" animation.values = [0, 30*force, -30*force, 30*force, 0] animation.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] animation.timingFunction = getTimingFunction(curve: curve) animation.duration = CFTimeInterval(duration) animation.isAdditive = true animation.repeatCount = repeatCount animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) layer.add(animation, forKey: "shake") case .Pop: let animation = CAKeyframeAnimation() animation.keyPath = "transform.scale" animation.values = [0, 0.2*force, -0.2*force, 0.2*force, 0] animation.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] animation.timingFunction = getTimingFunction(curve: curve) animation.duration = CFTimeInterval(duration) animation.isAdditive = true animation.repeatCount = repeatCount animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) layer.add(animation, forKey: "pop") case .FlipX: rotate = 0 scaleX = 1 scaleY = 1 var perspective = CATransform3DIdentity perspective.m34 = -1.0 / layer.frame.size.width/2 let animation = CABasicAnimation() animation.keyPath = "transform" animation.fromValue = NSValue(caTransform3D: CATransform3DMakeRotation(0, 0, 0, 0)) animation.toValue = NSValue(caTransform3D: CATransform3DConcat(perspective, CATransform3DMakeRotation(CGFloat(CGFloat.pi), 0, 1, 0))) animation.duration = CFTimeInterval(duration) animation.repeatCount = repeatCount animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) animation.timingFunction = getTimingFunction(curve: curve) layer.add(animation, forKey: "3d") case .FlipY: var perspective = CATransform3DIdentity perspective.m34 = -1.0 / layer.frame.size.width/2 let animation = CABasicAnimation() animation.keyPath = "transform" animation.fromValue = NSValue(caTransform3D: CATransform3DMakeRotation(0, 0, 0, 0)) animation.toValue = NSValue(caTransform3D: CATransform3DConcat(perspective,CATransform3DMakeRotation(CGFloat(CGFloat.pi), 1, 0, 0))) animation.duration = CFTimeInterval(duration) animation.repeatCount = repeatCount animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) animation.timingFunction = getTimingFunction(curve: curve) layer.add(animation, forKey: "3d") case .Morph: let morphX = CAKeyframeAnimation() morphX.keyPath = "transform.scale.x" morphX.values = [1, 1.3*force, 0.7, 1.3*force, 1] morphX.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] morphX.timingFunction = getTimingFunction(curve: curve) morphX.duration = CFTimeInterval(duration) morphX.repeatCount = repeatCount morphX.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) layer.add(morphX, forKey: "morphX") let morphY = CAKeyframeAnimation() morphY.keyPath = "transform.scale.y" morphY.values = [1, 0.7, 1.3*force, 0.7, 1] morphY.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] morphY.timingFunction = getTimingFunction(curve: curve) morphY.duration = CFTimeInterval(duration) morphY.repeatCount = repeatCount morphY.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) layer.add(morphY, forKey: "morphY") case .Squeeze: let morphX = CAKeyframeAnimation() morphX.keyPath = "transform.scale.x" morphX.values = [1, 1.5*force, 0.5, 1.5*force, 1] morphX.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] morphX.timingFunction = getTimingFunction(curve: curve) morphX.duration = CFTimeInterval(duration) morphX.repeatCount = repeatCount morphX.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) layer.add(morphX, forKey: "morphX") let morphY = CAKeyframeAnimation() morphY.keyPath = "transform.scale.y" morphY.values = [1, 0.5, 1, 0.5, 1] morphY.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] morphY.timingFunction = getTimingFunction(curve: curve) morphY.duration = CFTimeInterval(duration) morphY.repeatCount = repeatCount morphY.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) layer.add(morphY, forKey: "morphY") case .Flash: let animation = CABasicAnimation() animation.keyPath = "opacity" animation.fromValue = 1 animation.toValue = 0 animation.duration = CFTimeInterval(duration) animation.repeatCount = repeatCount * 2.0 animation.autoreverses = true animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) layer.add(animation, forKey: "flash") case .Wobble: let animation = CAKeyframeAnimation() animation.keyPath = "transform.rotation" animation.values = [0, 0.3*force, -0.3*force, 0.3*force, 0] animation.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] animation.duration = CFTimeInterval(duration) animation.isAdditive = true animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) layer.add(animation, forKey: "wobble") let x = CAKeyframeAnimation() x.keyPath = "position.x" x.values = [0, 30*force, -30*force, 30*force, 0] x.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] x.timingFunction = getTimingFunction(curve: curve) x.duration = CFTimeInterval(duration) x.isAdditive = true x.repeatCount = repeatCount x.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) layer.add(x, forKey: "x") case .Swing: let animation = CAKeyframeAnimation() animation.keyPath = "transform.rotation" animation.values = [0, 0.3*force, -0.3*force, 0.3*force, 0] animation.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1] animation.duration = CFTimeInterval(duration) animation.isAdditive = true animation.repeatCount = repeatCount animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) layer.add(animation, forKey: "swing") } } } func getTimingFunction(curve: String) -> CAMediaTimingFunction { if let curve = AnimationCurve(rawValue: curve) { switch curve { case .EaseIn: return CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn) case .EaseOut: return CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) case .EaseInOut: return CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) case .Linear: return CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) case .Spring: return CAMediaTimingFunction(controlPoints: 0.5, 1.1+Float(force/3), 1, 1) case .EaseInSine: return CAMediaTimingFunction(controlPoints: 0.47, 0, 0.745, 0.715) case .EaseOutSine: return CAMediaTimingFunction(controlPoints: 0.39, 0.575, 0.565, 1) case .EaseInOutSine: return CAMediaTimingFunction(controlPoints: 0.445, 0.05, 0.55, 0.95) case .EaseInQuad: return CAMediaTimingFunction(controlPoints: 0.55, 0.085, 0.68, 0.53) case .EaseOutQuad: return CAMediaTimingFunction(controlPoints: 0.25, 0.46, 0.45, 0.94) case .EaseInOutQuad: return CAMediaTimingFunction(controlPoints: 0.455, 0.03, 0.515, 0.955) case .EaseInCubic: return CAMediaTimingFunction(controlPoints: 0.55, 0.055, 0.675, 0.19) case .EaseOutCubic: return CAMediaTimingFunction(controlPoints: 0.215, 0.61, 0.355, 1) case .EaseInOutCubic: return CAMediaTimingFunction(controlPoints: 0.645, 0.045, 0.355, 1) case .EaseInQuart: return CAMediaTimingFunction(controlPoints: 0.895, 0.03, 0.685, 0.22) case .EaseOutQuart: return CAMediaTimingFunction(controlPoints: 0.165, 0.84, 0.44, 1) case .EaseInOutQuart: return CAMediaTimingFunction(controlPoints: 0.77, 0, 0.175, 1) case .EaseInQuint: return CAMediaTimingFunction(controlPoints: 0.755, 0.05, 0.855, 0.06) case .EaseOutQuint: return CAMediaTimingFunction(controlPoints: 0.23, 1, 0.32, 1) case .EaseInOutQuint: return CAMediaTimingFunction(controlPoints: 0.86, 0, 0.07, 1) case .EaseInExpo: return CAMediaTimingFunction(controlPoints: 0.95, 0.05, 0.795, 0.035) case .EaseOutExpo: return CAMediaTimingFunction(controlPoints: 0.19, 1, 0.22, 1) case .EaseInOutExpo: return CAMediaTimingFunction(controlPoints: 1, 0, 0, 1) case .EaseInCirc: return CAMediaTimingFunction(controlPoints: 0.6, 0.04, 0.98, 0.335) case .EaseOutCirc: return CAMediaTimingFunction(controlPoints: 0.075, 0.82, 0.165, 1) case .EaseInOutCirc: return CAMediaTimingFunction(controlPoints: 0.785, 0.135, 0.15, 0.86) case .EaseInBack: return CAMediaTimingFunction(controlPoints: 0.6, -0.28, 0.735, 0.045) case .EaseOutBack: return CAMediaTimingFunction(controlPoints: 0.175, 0.885, 0.32, 1.275) case .EaseInOutBack: return CAMediaTimingFunction(controlPoints: 0.68, -0.55, 0.265, 1.55) } } return CAMediaTimingFunction(name: CAMediaTimingFunctionName.default) } func getAnimationOptions(curve: String) -> UIView.AnimationOptions { if let curve = AnimationCurve(rawValue: curve) { switch curve { case .EaseIn: return UIView.AnimationOptions.curveEaseIn case .EaseOut: return UIView.AnimationOptions.curveEaseOut case .EaseInOut: return UIView.AnimationOptions() default: break } } return UIView.AnimationOptions.curveLinear } public func animate() { animateFrom = true animatePreset() setView {} } public func animateNext(completion: @escaping () -> ()) { animateFrom = true animatePreset() setView { completion() } } public func animateTo() { animateFrom = false animatePreset() setView {} } public func animateToNext(completion: @escaping () -> ()) { animateFrom = false animatePreset() setView { completion() } } public func customAwakeFromNib() { if autohide { alpha = 0 } } public func customLayoutSubviews() { if shouldAnimateInLayoutSubviews { shouldAnimateInLayoutSubviews = false if autostart { if UIApplication.shared.applicationState != .active { shouldAnimateAfterActive = true return } alpha = 0 animate() } } } func setView(completion: @escaping () -> ()) { if animateFrom { let translate = CGAffineTransform(translationX: self.x, y: self.y) let scale = CGAffineTransform(scaleX: self.scaleX, y: self.scaleY) let rotate = CGAffineTransform(rotationAngle: self.rotate) let translateAndScale = translate.concatenating(scale) self.transform = rotate.concatenating(translateAndScale) self.alpha = self.opacity } UIView.animate( withDuration: TimeInterval(duration), delay: TimeInterval(delay), usingSpringWithDamping: damping, initialSpringVelocity: velocity, options: [getAnimationOptions(curve: curve), UIView.AnimationOptions.allowUserInteraction], animations: { [weak self] in if let _self = self { if _self.animateFrom { _self.transform = CGAffineTransform.identity _self.alpha = 1 } else { let translate = CGAffineTransform(translationX: _self.x, y: _self.y) let scale = CGAffineTransform(scaleX: _self.scaleX, y: _self.scaleY) let rotate = CGAffineTransform(rotationAngle: _self.rotate) let translateAndScale = translate.concatenating(scale) _self.transform = rotate.concatenating(translateAndScale) _self.alpha = _self.opacity } } }, completion: { [weak self] finished in completion() self?.resetAll() }) } func reset() { x = 0 y = 0 opacity = 1 } func resetAll() { x = 0 y = 0 animation = "" opacity = 1 scaleX = 1 scaleY = 1 rotate = 0 damping = 0.7 velocity = 0.7 repeatCount = 1 delay = 0 duration = 0.7 } } ================================================ FILE: Spring/SpringAnimation.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit @objc public class SpringAnimation: NSObject { public class func spring(duration: TimeInterval, animations: @escaping () -> Void) { UIView.animate( withDuration: duration, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.7, options: [], animations: { animations() }, completion: nil ) } public class func springEaseIn(duration: TimeInterval, animations: (() -> Void)!) { UIView.animate( withDuration: duration, delay: 0, options: .curveEaseIn, animations: { animations() }, completion: nil ) } public class func springEaseOut(duration: TimeInterval, animations: (() -> Void)!) { UIView.animate( withDuration: duration, delay: 0, options: .curveEaseOut, animations: { animations() }, completion: nil ) } public class func springEaseInOut(duration: TimeInterval, animations: (() -> Void)!) { UIView.animate( withDuration: duration, delay: 0, options: UIView.AnimationOptions(), animations: { animations() }, completion: nil ) } public class func springLinear(duration: TimeInterval, animations: (() -> Void)!) { UIView.animate( withDuration: duration, delay: 0, options: .curveLinear, animations: { animations() }, completion: nil ) } public class func springWithDelay(duration: TimeInterval, delay: TimeInterval, animations: (() -> Void)!) { UIView.animate( withDuration: duration, delay: delay, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.7, options: [], animations: { animations() }, completion: nil ) } public class func springWithCompletion(duration: TimeInterval, animations: (() -> Void)!, completion: ((Bool) -> Void)!) { UIView.animate( withDuration: duration, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.7, options: [], animations: { animations() }, completion: { finished in completion(finished) } ) } } ================================================ FILE: Spring/SpringButton.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit open class SpringButton: UIButton, Springable { @IBInspectable public var autostart: Bool = false @IBInspectable public var autohide: Bool = false @IBInspectable public var animation: String = "" @IBInspectable public var force: CGFloat = 1 @IBInspectable public var delay: CGFloat = 0 @IBInspectable public var duration: CGFloat = 0.7 @IBInspectable public var damping: CGFloat = 0.7 @IBInspectable public var velocity: CGFloat = 0.7 @IBInspectable public var repeatCount: Float = 1 @IBInspectable public var x: CGFloat = 0 @IBInspectable public var y: CGFloat = 0 @IBInspectable public var scaleX: CGFloat = 1 @IBInspectable public var scaleY: CGFloat = 1 @IBInspectable public var rotate: CGFloat = 0 @IBInspectable public var curve: String = "" public var opacity: CGFloat = 1 public var animateFrom: Bool = false lazy private var spring : Spring = Spring(self) override open func awakeFromNib() { super.awakeFromNib() self.spring.customAwakeFromNib() } open override func layoutSubviews() { super.layoutSubviews() spring.customLayoutSubviews() } public func animate() { self.spring.animate() } public func animateNext(completion: @escaping () -> ()) { self.spring.animateNext(completion: completion) } public func animateTo() { self.spring.animateTo() } public func animateToNext(completion: @escaping () -> ()) { self.spring.animateToNext(completion: completion) } } ================================================ FILE: Spring/SpringImageView.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit open class SpringImageView: UIImageView, Springable { @IBInspectable public var autostart: Bool = false @IBInspectable public var autohide: Bool = false @IBInspectable public var animation: String = "" @IBInspectable public var force: CGFloat = 1 @IBInspectable public var delay: CGFloat = 0 @IBInspectable public var duration: CGFloat = 0.7 @IBInspectable public var damping: CGFloat = 0.7 @IBInspectable public var velocity: CGFloat = 0.7 @IBInspectable public var repeatCount: Float = 1 @IBInspectable public var x: CGFloat = 0 @IBInspectable public var y: CGFloat = 0 @IBInspectable public var scaleX: CGFloat = 1 @IBInspectable public var scaleY: CGFloat = 1 @IBInspectable public var rotate: CGFloat = 0 @IBInspectable public var curve: String = "" public var opacity: CGFloat = 1 public var animateFrom: Bool = false lazy private var spring : Spring = Spring(self) override open func awakeFromNib() { super.awakeFromNib() self.spring.customAwakeFromNib() } open override func layoutSubviews() { super.layoutSubviews() spring.customLayoutSubviews() } public func animate() { self.spring.animate() } public func animateNext(completion: @escaping () -> ()) { self.spring.animateNext(completion: completion) } public func animateTo() { self.spring.animateTo() } public func animateToNext(completion: @escaping () -> ()) { self.spring.animateToNext(completion: completion) } } ================================================ FILE: Spring/SpringLabel.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit open class SpringLabel: UILabel, Springable { @IBInspectable public var autostart: Bool = false @IBInspectable public var autohide: Bool = false @IBInspectable public var animation: String = "" @IBInspectable public var force: CGFloat = 1 @IBInspectable public var delay: CGFloat = 0 @IBInspectable public var duration: CGFloat = 0.7 @IBInspectable public var damping: CGFloat = 0.7 @IBInspectable public var velocity: CGFloat = 0.7 @IBInspectable public var repeatCount: Float = 1 @IBInspectable public var x: CGFloat = 0 @IBInspectable public var y: CGFloat = 0 @IBInspectable public var scaleX: CGFloat = 1 @IBInspectable public var scaleY: CGFloat = 1 @IBInspectable public var rotate: CGFloat = 0 @IBInspectable public var curve: String = "" public var opacity: CGFloat = 1 public var animateFrom: Bool = false lazy private var spring : Spring = Spring(self) override open func awakeFromNib() { super.awakeFromNib() self.spring.customAwakeFromNib() } open override func layoutSubviews() { super.layoutSubviews() spring.customLayoutSubviews() } public func animate() { self.spring.animate() } public func animateNext(completion: @escaping () -> ()) { self.spring.animateNext(completion: completion) } public func animateTo() { self.spring.animateTo() } public func animateToNext(completion: @escaping () -> ()) { self.spring.animateToNext(completion: completion) } } ================================================ FILE: Spring/SpringTextField.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit open class SpringTextField: UITextField, Springable { @IBInspectable public var autostart: Bool = false @IBInspectable public var autohide: Bool = false @IBInspectable public var animation: String = "" @IBInspectable public var force: CGFloat = 1 @IBInspectable public var delay: CGFloat = 0 @IBInspectable public var duration: CGFloat = 0.7 @IBInspectable public var damping: CGFloat = 0.7 @IBInspectable public var velocity: CGFloat = 0.7 @IBInspectable public var repeatCount: Float = 1 @IBInspectable public var x: CGFloat = 0 @IBInspectable public var y: CGFloat = 0 @IBInspectable public var scaleX: CGFloat = 1 @IBInspectable public var scaleY: CGFloat = 1 @IBInspectable public var rotate: CGFloat = 0 @IBInspectable public var curve: String = "" public var opacity: CGFloat = 1 public var animateFrom: Bool = false lazy private var spring : Spring = Spring(self) override open func awakeFromNib() { super.awakeFromNib() self.spring.customAwakeFromNib() } open override func layoutSubviews() { super.layoutSubviews() spring.customLayoutSubviews() } public func animate() { self.spring.animate() } public func animateNext(completion: @escaping () -> ()) { self.spring.animateNext(completion: completion) } public func animateTo() { self.spring.animateTo() } public func animateToNext(completion: @escaping () -> ()) { self.spring.animateToNext(completion: completion) } } ================================================ FILE: Spring/SpringTextView.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit open class SpringTextView: UITextView, Springable { @IBInspectable public var autostart: Bool = false @IBInspectable public var autohide: Bool = false @IBInspectable public var animation: String = "" @IBInspectable public var force: CGFloat = 1 @IBInspectable public var delay: CGFloat = 0 @IBInspectable public var duration: CGFloat = 0.7 @IBInspectable public var damping: CGFloat = 0.7 @IBInspectable public var velocity: CGFloat = 0.7 @IBInspectable public var repeatCount: Float = 1 @IBInspectable public var x: CGFloat = 0 @IBInspectable public var y: CGFloat = 0 @IBInspectable public var scaleX: CGFloat = 1 @IBInspectable public var scaleY: CGFloat = 1 @IBInspectable public var rotate: CGFloat = 0 @IBInspectable public var curve: String = "" public var opacity: CGFloat = 1 public var animateFrom: Bool = false lazy private var spring : Spring = Spring(self) override open func awakeFromNib() { super.awakeFromNib() self.spring.customAwakeFromNib() } open override func layoutSubviews() { super.layoutSubviews() spring.customLayoutSubviews() } public func animate() { self.spring.animate() } public func animateNext(completion: @escaping () -> ()) { self.spring.animateNext(completion: completion) } public func animateTo() { self.spring.animateTo() } public func animateToNext(completion: @escaping () -> ()) { self.spring.animateToNext(completion: completion) } } ================================================ FILE: Spring/SpringView.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit open class SpringView: UIView, Springable { @IBInspectable public var autostart: Bool = false @IBInspectable public var autohide: Bool = false @IBInspectable public var animation: String = "" @IBInspectable public var force: CGFloat = 1 @IBInspectable public var delay: CGFloat = 0 @IBInspectable public var duration: CGFloat = 0.7 @IBInspectable public var damping: CGFloat = 0.7 @IBInspectable public var velocity: CGFloat = 0.7 @IBInspectable public var repeatCount: Float = 1 @IBInspectable public var x: CGFloat = 0 @IBInspectable public var y: CGFloat = 0 @IBInspectable public var scaleX: CGFloat = 1 @IBInspectable public var scaleY: CGFloat = 1 @IBInspectable public var rotate: CGFloat = 0 @IBInspectable public var curve: String = "" public var opacity: CGFloat = 1 public var animateFrom: Bool = false lazy private var spring : Spring = Spring(self) override open func awakeFromNib() { super.awakeFromNib() self.spring.customAwakeFromNib() } open override func layoutSubviews() { super.layoutSubviews() spring.customLayoutSubviews() } public func animate() { self.spring.animate() } public func animateNext(completion: @escaping () -> ()) { self.spring.animateNext(completion: completion) } public func animateTo() { self.spring.animateTo() } public func animateToNext(completion: @escaping () -> ()) { self.spring.animateToNext(completion: completion) } } ================================================ FILE: Spring/TransitionManager.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit public class TransitionManager: NSObject, UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning { var isPresenting = true var duration = 0.3 public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let container = transitionContext.containerView let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)! let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)! if isPresenting { toView.frame = container.bounds toView.transform = CGAffineTransform(translationX: 0, y: container.frame.size.height) container.addSubview(fromView) container.addSubview(toView) SpringAnimation.springEaseInOut(duration: duration) { fromView.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) fromView.alpha = 0.5 toView.transform = CGAffineTransform.identity } } else { // 1. Rotating will change the bounds // 2. we have to properly reset toView // to the actual container's bounds, at // the same time take consideration of // previous transformation when presenting let transform = toView.transform toView.transform = CGAffineTransform.identity toView.frame = container.bounds toView.transform = transform container.addSubview(toView) container.addSubview(fromView) SpringAnimation.springEaseInOut(duration: duration) { fromView.transform = CGAffineTransform(translationX: 0, y: fromView.frame.size.height) toView.transform = CGAffineTransform.identity toView.alpha = 1 } } delay(delay: duration, closure: { transitionContext.completeTransition(true) }) } public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return duration } public func animationController(forPresentedController presented: UIViewController, presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { isPresenting = true return self } public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { isPresenting = false return self } } ================================================ FILE: Spring/TransitionZoom.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit public class TransitionZoom: NSObject, UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning { var isPresenting = true var duration = 0.4 public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let container = transitionContext.containerView let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)! let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)! if isPresenting { container.addSubview(fromView) container.addSubview(toView) toView.alpha = 0 toView.transform = CGAffineTransform(scaleX: 2, y: 2) SpringAnimation.springEaseInOut(duration: duration) { fromView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5) fromView.alpha = 0 toView.transform = CGAffineTransform.identity toView.alpha = 1 } } else { container.addSubview(toView) container.addSubview(fromView) SpringAnimation.springEaseInOut(duration: duration) { fromView.transform = CGAffineTransform(scaleX: 2, y: 2) fromView.alpha = 0 toView.transform = CGAffineTransform(scaleX: 1, y: 1) toView.alpha = 1 } } delay(delay: duration, closure: { transitionContext.completeTransition(true) }) } public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return duration } public func animationController(forPresentedController presented: UIViewController, presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { isPresenting = true return self } public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { isPresenting = false return self } } ================================================ FILE: Spring/UnwindSegue.swift ================================================ // The MIT License (MIT) // // Copyright (c) 2015 Meng To (meng@designcode.io) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit public extension UIViewController { @IBAction public func unwindToViewController (_ segue: UIStoryboardSegue){} } ================================================ FILE: Spring.podspec ================================================ Pod::Spec.new do |s| s.name = 'Spring' s.version = '1.0.6' s.license = 'MIT' s.summary = 'A library to simplify iOS animations in Swift.' s.homepage = 'https://github.com/MengTo/Spring' s.authors = { 'Meng To' => 'meng@designcode.io' } s.source = { :git => 'https://github.com/MengTo/Spring.git', :tag => s.version.to_s } s.requires_arc = true s.ios.deployment_target = '8.0' s.tvos.deployment_target = '11.0' s.source_files = 'Spring/*.swift' s.ios.resources = ['Spring/*.xib', 'SpringApp/*.xcassets'] s.tvos.resources = ['SpringApp/*.xcassets'] end ================================================ FILE: SpringApp/AppDelegate.swift ================================================ // // AppDelegate.swift // SpringApp // // Created by Meng To on 2015-01-06. // Copyright (c) 2015 Meng To. All rights reserved. // import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func applicationDidFinishLaunching(_ application: UIApplication) { // Override point for customization after application launch. } func applicationWillResignActive(_ application: UIApplication) { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. } func applicationDidEnterBackground(_ application: UIApplication) { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } func applicationWillEnterForeground(_ application: UIApplication) { // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. } func applicationDidBecomeActive(_ application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } } ================================================ FILE: SpringApp/Base.lproj/LaunchScreen.xib ================================================ ================================================ FILE: SpringApp/Base.lproj/Main.storyboard ================================================ AvenirNext-DemiBold AvenirNext-Regular ================================================ FILE: SpringApp/CodeViewController.swift ================================================ // // CodeViewController.swift // DesignerNewsApp // // Created by Meng To on 2015-01-05. // Copyright (c) 2015 Meng To. All rights reserved. // import UIKit import Spring class CodeViewController: UIViewController { @IBOutlet weak var modalView: SpringView! @IBOutlet weak var codeTextView: UITextView! @IBOutlet weak var titleLabel: UILabel! var codeText: String = "" var data: SpringView! override func viewDidLoad() { super.viewDidLoad() modalView.transform = CGAffineTransform(translationX: -300, y: 0) if data.animation != "" { codeText += "layer.animation = \"\(data.animation)\"\n" } if data.curve != "" { codeText += "layer.curve = \"\(data.curve)\"\n" } if data.force != 1 { codeText += String(format: "layer.force = %.1f\n", Double(data.force)) } if data.duration != 0.7 { codeText += String(format: "layer.duration = %.1f\n", Double(data.duration)) } if data.delay != 0 { codeText += String(format: "layer.delay = %.1f\n", Double(data.delay)) } if data.scaleX != 1 { codeText += String(format: "layer.scaleX = %.1f\n", Double(data.scaleX)) } if data.scaleY != 1 { codeText += String(format: "layer.scaleY = %.1f\n", Double(data.scaleY)) } if data.rotate != 0 { codeText += String(format: "layer.rotate = %.1f\n", Double(data.rotate)) } if data.damping != 0.7 { codeText += String(format: "layer.damping = %.1f\n", Double(data.damping)) } if data.velocity != 0.7 { codeText += String(format: "layer.velocity = %.1f\n", Double(data.velocity)) } codeText += "layer.animate()" codeTextView.text = codeText } @IBAction func closeButtonPressed(_ sender: AnyObject) { UIApplication.shared.sendAction(#selector(SpringViewController.maximizeView(_:)), to: nil, from: self, for: nil) modalView.animation = "slideRight" modalView.animateFrom = false modalView.animateToNext(completion: { self.dismiss(animated: false, completion: nil) }) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(true) modalView.animate() UIApplication.shared.sendAction(#selector(SpringViewController.minimizeView(_:)), to: nil, from: self, for: nil) } } ================================================ FILE: SpringApp/Images.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "size" : "29x29", "idiom" : "iphone", "filename" : "appicon@58.png", "scale" : "2x" }, { "size" : "29x29", "idiom" : "iphone", "filename" : "appicon@87.png", "scale" : "3x" }, { "size" : "40x40", "idiom" : "iphone", "filename" : "appicon@80.png", "scale" : "2x" }, { "size" : "40x40", "idiom" : "iphone", "filename" : "appicon@120.png", "scale" : "3x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "appicon@120-1.png", "scale" : "2x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "appicon@180.png", "scale" : "3x" }, { "size" : "29x29", "idiom" : "ipad", "filename" : "appicon@29.png", "scale" : "1x" }, { "size" : "29x29", "idiom" : "ipad", "filename" : "appicon@58-1.png", "scale" : "2x" }, { "size" : "40x40", "idiom" : "ipad", "filename" : "appicon@40.png", "scale" : "1x" }, { "size" : "40x40", "idiom" : "ipad", "filename" : "appicon@80-1.png", "scale" : "2x" }, { "size" : "76x76", "idiom" : "ipad", "filename" : "appicon@76.png", "scale" : "1x" }, { "size" : "76x76", "idiom" : "ipad", "filename" : "appicon@152.png", "scale" : "2x" }, { "size" : "83.5x83.5", "idiom" : "ipad", "filename" : "appicon@167.png", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: SpringApp/Images.xcassets/icon-shape.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "icon-shape.pdf" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: SpringApp/Images.xcassets/loading.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "loading.pdf" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: SpringApp/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName Spring CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 LSRequiresIPhoneOS UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIViewControllerBasedStatusBarAppearance ================================================ FILE: SpringApp/OptionsViewController.swift ================================================ // // OptionsViewController.swift // DesignerNewsApp // // Created by Meng To on 2015-01-04. // Copyright (c) 2015 Meng To. All rights reserved. // import UIKit import Spring protocol OptionsViewControllerDelegate: class { func dampingSliderChanged(_ sender: AnyObject) func velocitySliderChanged(_ sender: AnyObject) func scaleSliderChanged(_ sender: AnyObject) func xSliderChanged(_ sender: AnyObject) func ySliderChanged(_ sender: AnyObject) func rotateSliderChanged(_ sender: AnyObject) func resetButtonPressed(_ sender: AnyObject) } class OptionsViewController: UIViewController { @IBOutlet weak var modalView: SpringView! @IBOutlet weak var dampingLabel: UILabel! @IBOutlet weak var velocityLabel: UILabel! @IBOutlet weak var scaleLabel: UILabel! @IBOutlet weak var xLabel: UILabel! @IBOutlet weak var yLabel: UILabel! @IBOutlet weak var rotateLabel: UILabel! @IBOutlet weak var dampingSlider: UISlider! @IBOutlet weak var velocitySlider: UISlider! @IBOutlet weak var scaleSlider: UISlider! @IBOutlet weak var xSlider: UISlider! @IBOutlet weak var ySlider: UISlider! @IBOutlet weak var rotateSlider: UISlider! var selectedDamping: CGFloat = 0.7 var selectedVelocity: CGFloat = 0.7 var selectedScale: CGFloat = 1 var selectedX: CGFloat = 0 var selectedY: CGFloat = 0 var selectedRotate: CGFloat = 0 weak var delegate: OptionsViewControllerDelegate? var data: SpringView! override func viewDidLoad() { super.viewDidLoad() modalView.transform = CGAffineTransform(translationX: 0, y: 300) dampingSlider.setValue(Float(data.damping), animated: true) velocitySlider.setValue(Float(data.velocity), animated: true) scaleSlider.setValue(Float(data.scaleX), animated: true) xSlider.setValue(Float(data.x), animated: true) ySlider.setValue(Float(data.y), animated: true) rotateSlider.setValue(Float(data.rotate), animated: true) dampingLabel.text = getString("Damping", value: data.damping) velocityLabel.text = getString("Velocity", value: data.velocity) scaleLabel.text = getString("Scale", value: data.scaleX) xLabel.text = getString("x", value: data.x) yLabel.text = getString("y", value: data.y) rotateLabel.text = getString("Rotate", value: data.rotate) } @IBAction func dampingSliderChanged(_ sender: AnyObject) { selectedDamping = sender.value(forKey: "value") as! CGFloat delegate?.dampingSliderChanged(sender) dampingLabel.text = getString("Damping", value: selectedDamping) } @IBAction func velocitySliderChanged(_ sender: AnyObject) { selectedVelocity = sender.value(forKey: "value") as! CGFloat delegate?.velocitySliderChanged(sender) velocityLabel.text = getString("Velocity", value: selectedVelocity) } @IBAction func scaleSliderChanged(_ sender: AnyObject) { selectedScale = sender.value(forKey: "value") as! CGFloat delegate?.scaleSliderChanged(sender) scaleLabel.text = getString("Scale", value: selectedScale) } @IBAction func xSliderChanged(_ sender: AnyObject) { selectedX = sender.value(forKey: "value") as! CGFloat delegate?.xSliderChanged(sender) xLabel.text = getString("X", value: selectedX) } @IBAction func ySliderChanged(_ sender: AnyObject) { selectedY = sender.value(forKey: "value") as! CGFloat delegate?.ySliderChanged(sender) yLabel.text = getString("Y", value: selectedY) } @IBAction func rotateSliderChanged(_ sender: AnyObject) { selectedRotate = sender.value(forKey: "value") as! CGFloat delegate?.rotateSliderChanged(sender) rotateLabel.text = getString("Rotate", value: selectedRotate) } @IBAction func resetButtonPressed(_ sender: AnyObject) { delegate?.resetButtonPressed(sender) dismiss(animated: true, completion: nil) UIApplication.shared.sendAction(#selector(SpringViewController.maximizeView(_:)), to: nil, from: self, for: nil) } @IBAction func closeButtonPressed(_ sender: AnyObject) { dismiss(animated: true, completion: nil) UIApplication.shared.sendAction(#selector(SpringViewController.maximizeView(_:)), to: nil, from: self, for: nil) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(true) UIApplication.shared.sendAction(#selector(SpringViewController.minimizeView(_:)), to: nil, from: self, for: nil) modalView.animate() } func getString(_ name: String, value: CGFloat) -> String { return String(format: "\(name): %.1f", Double(value)) } } ================================================ FILE: SpringApp/SpringViewController.swift ================================================ // // SpringViewController.swift // DesignerNewsApp // // Created by Meng To on 2015-01-02. // Copyright (c) 2015 Meng To. All rights reserved. // import UIKit import Spring class SpringViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, OptionsViewControllerDelegate { @IBOutlet weak var delayLabel: UILabel! @IBOutlet weak var durationLabel: UILabel! @IBOutlet weak var forceLabel: UILabel! @IBOutlet weak var delaySlider: UISlider! @IBOutlet weak var durationSlider: UISlider! @IBOutlet weak var forceSlider: UISlider! @IBOutlet weak var ballView: SpringView! @IBOutlet weak var animationPicker: UIPickerView! var selectedRow: Int = 0 var selectedEasing: Int = 0 var selectedForce: CGFloat = 1 var selectedDuration: CGFloat = 1 var selectedDelay: CGFloat = 0 var selectedDamping: CGFloat = 0.7 var selectedVelocity: CGFloat = 0.7 var selectedScale: CGFloat = 1 var selectedX: CGFloat = 0 var selectedY: CGFloat = 0 var selectedRotate: CGFloat = 0 @IBAction func forceSliderChanged(_ sender: AnyObject) { selectedForce = sender.value(forKey: "value") as! CGFloat animateView() forceLabel.text = String(format: "Force: %.1f", Double(selectedForce)) } @IBAction func durationSliderChanged(_ sender: AnyObject) { selectedDuration = sender.value(forKey: "value") as! CGFloat animateView() durationLabel.text = String(format: "Duration: %.1f", Double(selectedDuration)) } @IBAction func delaySliderChanged(_ sender: AnyObject) { selectedDelay = sender.value(forKey: "value") as! CGFloat animateView() delayLabel.text = String(format: "Delay: %.1f", Double(selectedDelay)) } func dampingSliderChanged(_ sender: AnyObject) { selectedDamping = sender.value(forKey: "value") as! CGFloat animateView() } func velocitySliderChanged(_ sender: AnyObject) { selectedVelocity = sender.value(forKey: "value") as! CGFloat animateView() } func scaleSliderChanged(_ sender: AnyObject) { selectedScale = sender.value(forKey: "value") as! CGFloat animateView() } func xSliderChanged(_ sender: AnyObject) { selectedX = sender.value(forKey: "value") as! CGFloat animateView() } func ySliderChanged(_ sender: AnyObject) { selectedY = sender.value(forKey: "value") as! CGFloat animateView() } func rotateSliderChanged(_ sender: AnyObject) { selectedRotate = sender.value(forKey: "value") as! CGFloat animateView() } func animateView() { setOptions() ballView.animate() } func setOptions() { ballView.force = selectedForce ballView.duration = selectedDuration ballView.delay = selectedDelay ballView.damping = selectedDamping ballView.velocity = selectedVelocity ballView.scaleX = selectedScale ballView.scaleY = selectedScale ballView.x = selectedX ballView.y = selectedY ballView.rotate = selectedRotate ballView.animation = animations[selectedRow].rawValue ballView.curve = animationCurves[selectedEasing].rawValue } @objc func minimizeView(_ sender: AnyObject) { SpringAnimation.spring(duration: 0.7, animations: { self.view.transform = CGAffineTransform(scaleX: 0.935, y: 0.935) }) UIApplication.shared.setStatusBarStyle(UIStatusBarStyle.lightContent, animated: true) } @objc func maximizeView(_ sender: AnyObject) { SpringAnimation.spring(duration: 0.7, animations: { self.view.transform = CGAffineTransform(scaleX: 1, y: 1) }) UIApplication.shared.setStatusBarStyle(UIStatusBarStyle.default, animated: true) } let animations: [Spring.AnimationPreset] = [ .Shake, .Pop, .Morph, .Squeeze, .Wobble, .Swing, .FlipX, .FlipY, .Fall, .SqueezeLeft, .SqueezeRight, .SqueezeDown, .SqueezeUp, .SlideLeft, .SlideRight, .SlideDown, .SlideUp, .FadeIn, .FadeOut, .FadeInLeft, .FadeInRight, .FadeInDown, .FadeInUp, .ZoomIn, .ZoomOut, .Flash ] var animationCurves: [Spring.AnimationCurve] = [ .EaseIn, .EaseOut, .EaseInOut, .Linear, .Spring, .EaseInSine, .EaseOutSine, .EaseInOutSine, .EaseInQuad, .EaseOutQuad, .EaseInOutQuad, .EaseInCubic, .EaseOutCubic, .EaseInOutCubic, .EaseInQuart, .EaseOutQuart, .EaseInOutQuart, .EaseInQuint, .EaseOutQuint, .EaseInOutQuint, .EaseInExpo, .EaseOutExpo, .EaseInOutExpo, .EaseInCirc, .EaseOutCirc, .EaseInOutCirc, .EaseInBack, .EaseOutBack, .EaseInOutBack ] override func viewDidLoad() { super.viewDidLoad() animationPicker.delegate = self animationPicker.dataSource = self animationPicker.showsSelectionIndicator = true } @IBAction func ballButtonPressed(_ sender: AnyObject) { UIView.animate(withDuration: 0.1, animations: { self.ballView.backgroundColor = UIColor(hex: "69DBFF") }, completion: { finished in UIView.animate(withDuration: 0.5, animations: { self.ballView.backgroundColor = UIColor(hex: "#279CEB") }) }) animateView() } var isBall = false func changeBall() { isBall = !isBall let animation = CABasicAnimation() let halfWidth = ballView.frame.width / 2 let cornerRadius: CGFloat = isBall ? halfWidth : 10 animation.keyPath = "cornerRadius" animation.fromValue = isBall ? 10 : halfWidth animation.toValue = cornerRadius animation.duration = 0.2 ballView.layer.cornerRadius = cornerRadius ballView.layer.add(animation, forKey: "radius") } @IBAction func shapeButtonPressed(_ sender: AnyObject) { changeBall() } func resetButtonPressed(_ sender: AnyObject) { selectedForce = 1 selectedDuration = 1 selectedDelay = 0 selectedDamping = 0.7 selectedVelocity = 0.7 selectedScale = 1 selectedX = 0 selectedY = 0 selectedRotate = 0 forceSlider.setValue(Float(selectedForce), animated: true) durationSlider.setValue(Float(selectedDuration), animated: true) delaySlider.setValue(Float(selectedDelay), animated: true) forceLabel.text = String(format: "Force: %.1f", Double(selectedForce)) durationLabel.text = String(format: "Duration: %.1f", Double(selectedDuration)) delayLabel.text = String(format: "Delay: %.1f", Double(selectedDelay)) } func numberOfComponents(in pickerView: UIPickerView) -> Int { return 2 } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return component == 0 ? animations.count : animationCurves.count } func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return component == 0 ? animations[row].rawValue : animationCurves[row].rawValue } func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { switch component { case 0: selectedRow = row animateView() default: selectedEasing = row animateView() } } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let optionsViewController = segue.destination as? OptionsViewController { optionsViewController.delegate = self setOptions() optionsViewController.data = ballView } else if let codeViewController = segue.destination as? CodeViewController { setOptions() codeViewController.data = ballView } } } ================================================ FILE: SpringApp.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 1A4FDA381A6E44780099D309 /* Spring.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A4FDA371A6E44780099D309 /* Spring.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1A4FDA3E1A6E44780099D309 /* Spring.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A4FDA331A6E44780099D309 /* Spring.framework */; }; 1A4FDA471A6E44780099D309 /* SpringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A4FDA461A6E44780099D309 /* SpringTests.swift */; }; 1A4FDA4A1A6E44780099D309 /* Spring.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A4FDA331A6E44780099D309 /* Spring.framework */; }; 1A4FDA4B1A6E44780099D309 /* Spring.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 1A4FDA331A6E44780099D309 /* Spring.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 1A4FDA531A6E44A70099D309 /* LoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A4FDA2A1A6E44270099D309 /* LoadingView.swift */; }; 1A4FDA541A6E44A70099D309 /* LoadingView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1A4FDA2B1A6E44270099D309 /* LoadingView.xib */; }; 1A4FDA551A6E44A70099D309 /* BlurView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888D31A66BF9000295A64 /* BlurView.swift */; }; 1A4FDA571A6E44A70099D309 /* DesignableButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888D51A66BF9000295A64 /* DesignableButton.swift */; }; 1A4FDA581A6E44A70099D309 /* DesignableImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888D61A66BF9000295A64 /* DesignableImageView.swift */; }; 1A4FDA591A6E44A70099D309 /* DesignableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888D71A66BF9000295A64 /* DesignableLabel.swift */; }; 1A4FDA5A1A6E44A70099D309 /* DesignableTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888D81A66BF9000295A64 /* DesignableTextField.swift */; }; 1A4FDA5B1A6E44A70099D309 /* DesignableTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888D91A66BF9000295A64 /* DesignableTextView.swift */; }; 1A4FDA5C1A6E44A70099D309 /* DesignableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888DA1A66BF9000295A64 /* DesignableView.swift */; }; 1A4FDA5D1A6E44A70099D309 /* DesignableTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 969775D31A6AD6AC009B4B79 /* DesignableTabBarController.swift */; }; 1A4FDA5E1A6E44A70099D309 /* ImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888DB1A66BF9000295A64 /* ImageLoader.swift */; }; 1A4FDA601A6E44A70099D309 /* Misc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888DF1A66BF9000295A64 /* Misc.swift */; }; 1A4FDA611A6E44A70099D309 /* Spring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD08AC61A676D5800160D45 /* Spring.swift */; }; 1A4FDA621A6E44A70099D309 /* SpringAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888E01A66BF9000295A64 /* SpringAnimation.swift */; }; 1A4FDA631A6E44A70099D309 /* SpringButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888E11A66BF9000295A64 /* SpringButton.swift */; }; 1A4FDA641A6E44A70099D309 /* SpringImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF3F11A1A6776760090E8F9 /* SpringImageView.swift */; }; 1A4FDA651A6E44A70099D309 /* SpringLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF3F11B1A6776760090E8F9 /* SpringLabel.swift */; }; 1A4FDA661A6E44A70099D309 /* SpringTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF3F11C1A6776760090E8F9 /* SpringTextField.swift */; }; 1A4FDA671A6E44A70099D309 /* SpringTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF3F11D1A6776760090E8F9 /* SpringTextView.swift */; }; 1A4FDA681A6E44A70099D309 /* SpringView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888E21A66BF9000295A64 /* SpringView.swift */; }; 1A4FDA691A6E44A70099D309 /* TransitionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888E31A66BF9000295A64 /* TransitionManager.swift */; }; 1A4FDA6A1A6E44A70099D309 /* TransitionZoom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888E41A66BF9000295A64 /* TransitionZoom.swift */; }; 1A4FDA6B1A6E44A70099D309 /* UnwindSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888E51A66BF9000295A64 /* UnwindSegue.swift */; }; 1A585F401A7B9530007EEB7D /* KeyboardLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A585F3F1A7B9530007EEB7D /* KeyboardLayoutConstraint.swift */; }; 1A9F866D1A83C5640098BE6C /* SoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9F866C1A83C5640098BE6C /* SoundPlayer.swift */; }; 1AA7E1831AA36EFF00762D75 /* AsyncImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AA7E1811AA36EFF00762D75 /* AsyncImageView.swift */; }; 1AA7E1841AA36EFF00762D75 /* AsyncButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AA7E1821AA36EFF00762D75 /* AsyncButton.swift */; }; 1AB764641A6E4F070076CD78 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 964117471A5BE90A000E3A5A /* Images.xcassets */; }; 964117411A5BE90A000E3A5A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964117401A5BE90A000E3A5A /* AppDelegate.swift */; }; 964117461A5BE90A000E3A5A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 964117441A5BE90A000E3A5A /* Main.storyboard */; }; 964117481A5BE90A000E3A5A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 964117471A5BE90A000E3A5A /* Images.xcassets */; }; 9641174B1A5BE90A000E3A5A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 964117491A5BE90A000E3A5A /* LaunchScreen.xib */; }; 964117571A5BE90A000E3A5A /* SpringAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964117561A5BE90A000E3A5A /* SpringAppTests.swift */; }; 9641178B1A5BEC6F000E3A5A /* SpringViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964117881A5BEC6F000E3A5A /* SpringViewController.swift */; }; 9641178C1A5BEC6F000E3A5A /* OptionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964117891A5BEC6F000E3A5A /* OptionsViewController.swift */; }; 9641178D1A5BEC6F000E3A5A /* CodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9641178A1A5BEC6F000E3A5A /* CodeViewController.swift */; }; 96C5F4621AC464C800BB8A18 /* AutoTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C5F4611AC464C800BB8A18 /* AutoTextView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 1A4FDA3F1A6E44780099D309 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 964117331A5BE90A000E3A5A /* Project object */; proxyType = 1; remoteGlobalIDString = 1A4FDA321A6E44780099D309; remoteInfo = Spring; }; 1A4FDA411A6E44780099D309 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 964117331A5BE90A000E3A5A /* Project object */; proxyType = 1; remoteGlobalIDString = 9641173A1A5BE90A000E3A5A; remoteInfo = SpringApp; }; 1A4FDA481A6E44780099D309 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 964117331A5BE90A000E3A5A /* Project object */; proxyType = 1; remoteGlobalIDString = 1A4FDA321A6E44780099D309; remoteInfo = Spring; }; 964117511A5BE90A000E3A5A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 964117331A5BE90A000E3A5A /* Project object */; proxyType = 1; remoteGlobalIDString = 9641173A1A5BE90A000E3A5A; remoteInfo = SpringApp; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 1A4FDA4F1A6E44780099D309 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 1A4FDA4B1A6E44780099D309 /* Spring.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 1A4FDA2A1A6E44270099D309 /* LoadingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = ""; }; 1A4FDA2B1A6E44270099D309 /* LoadingView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LoadingView.xib; sourceTree = ""; }; 1A4FDA331A6E44780099D309 /* Spring.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Spring.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1A4FDA361A6E44780099D309 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 1A4FDA371A6E44780099D309 /* Spring.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Spring.h; sourceTree = ""; }; 1A4FDA3D1A6E44780099D309 /* SpringTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SpringTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 1A4FDA451A6E44780099D309 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 1A4FDA461A6E44780099D309 /* SpringTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpringTests.swift; sourceTree = ""; }; 1A585F3F1A7B9530007EEB7D /* KeyboardLayoutConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardLayoutConstraint.swift; sourceTree = ""; }; 1A9F866C1A83C5640098BE6C /* SoundPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SoundPlayer.swift; sourceTree = ""; }; 1AA7E1811AA36EFF00762D75 /* AsyncImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncImageView.swift; sourceTree = ""; }; 1AA7E1821AA36EFF00762D75 /* AsyncButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncButton.swift; sourceTree = ""; }; 1AD08AC61A676D5800160D45 /* Spring.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Spring.swift; sourceTree = ""; }; 1AF3F11A1A6776760090E8F9 /* SpringImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringImageView.swift; sourceTree = ""; }; 1AF3F11B1A6776760090E8F9 /* SpringLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringLabel.swift; sourceTree = ""; }; 1AF3F11C1A6776760090E8F9 /* SpringTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringTextField.swift; sourceTree = ""; }; 1AF3F11D1A6776760090E8F9 /* SpringTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringTextView.swift; sourceTree = ""; }; 961888D31A66BF9000295A64 /* BlurView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlurView.swift; sourceTree = ""; }; 961888D51A66BF9000295A64 /* DesignableButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DesignableButton.swift; sourceTree = ""; }; 961888D61A66BF9000295A64 /* DesignableImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DesignableImageView.swift; sourceTree = ""; }; 961888D71A66BF9000295A64 /* DesignableLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DesignableLabel.swift; sourceTree = ""; }; 961888D81A66BF9000295A64 /* DesignableTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DesignableTextField.swift; sourceTree = ""; }; 961888D91A66BF9000295A64 /* DesignableTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DesignableTextView.swift; sourceTree = ""; }; 961888DA1A66BF9000295A64 /* DesignableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DesignableView.swift; sourceTree = ""; }; 961888DB1A66BF9000295A64 /* ImageLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageLoader.swift; sourceTree = ""; }; 961888DF1A66BF9000295A64 /* Misc.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Misc.swift; sourceTree = ""; }; 961888E01A66BF9000295A64 /* SpringAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringAnimation.swift; sourceTree = ""; }; 961888E11A66BF9000295A64 /* SpringButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringButton.swift; sourceTree = ""; }; 961888E21A66BF9000295A64 /* SpringView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringView.swift; sourceTree = ""; }; 961888E31A66BF9000295A64 /* TransitionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionManager.swift; sourceTree = ""; }; 961888E41A66BF9000295A64 /* TransitionZoom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionZoom.swift; sourceTree = ""; }; 961888E51A66BF9000295A64 /* UnwindSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnwindSegue.swift; sourceTree = ""; }; 9641173B1A5BE90A000E3A5A /* SpringApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SpringApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 9641173F1A5BE90A000E3A5A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 964117401A5BE90A000E3A5A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 964117451A5BE90A000E3A5A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 964117471A5BE90A000E3A5A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 9641174A1A5BE90A000E3A5A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 964117501A5BE90A000E3A5A /* SpringAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SpringAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 964117551A5BE90A000E3A5A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 964117561A5BE90A000E3A5A /* SpringAppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpringAppTests.swift; sourceTree = ""; }; 964117881A5BEC6F000E3A5A /* SpringViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringViewController.swift; sourceTree = ""; }; 964117891A5BEC6F000E3A5A /* OptionsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OptionsViewController.swift; sourceTree = ""; }; 9641178A1A5BEC6F000E3A5A /* CodeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodeViewController.swift; sourceTree = ""; }; 969775D31A6AD6AC009B4B79 /* DesignableTabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DesignableTabBarController.swift; sourceTree = ""; }; 96C5F4611AC464C800BB8A18 /* AutoTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutoTextView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 1A4FDA2F1A6E44780099D309 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 1A4FDA3A1A6E44780099D309 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 1A4FDA3E1A6E44780099D309 /* Spring.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 964117381A5BE90A000E3A5A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 1A4FDA4A1A6E44780099D309 /* Spring.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 9641174D1A5BE90A000E3A5A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 1A4FDA341A6E44780099D309 /* Spring */ = { isa = PBXGroup; children = ( 1A4FDA371A6E44780099D309 /* Spring.h */, 1A4FDA351A6E44780099D309 /* Supporting Files */, ); path = Spring; sourceTree = ""; }; 1A4FDA351A6E44780099D309 /* Supporting Files */ = { isa = PBXGroup; children = ( 1A4FDA361A6E44780099D309 /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; 1A4FDA431A6E44780099D309 /* SpringTests */ = { isa = PBXGroup; children = ( 1A4FDA461A6E44780099D309 /* SpringTests.swift */, 1A4FDA441A6E44780099D309 /* Supporting Files */, ); path = SpringTests; sourceTree = ""; }; 1A4FDA441A6E44780099D309 /* Supporting Files */ = { isa = PBXGroup; children = ( 1A4FDA451A6E44780099D309 /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; 961888D21A66BF9000295A64 /* Spring */ = { isa = PBXGroup; children = ( 1AA7E1821AA36EFF00762D75 /* AsyncButton.swift */, 1AA7E1811AA36EFF00762D75 /* AsyncImageView.swift */, 96C5F4611AC464C800BB8A18 /* AutoTextView.swift */, 1A4FDA2A1A6E44270099D309 /* LoadingView.swift */, 1A4FDA2B1A6E44270099D309 /* LoadingView.xib */, 961888D31A66BF9000295A64 /* BlurView.swift */, 961888D51A66BF9000295A64 /* DesignableButton.swift */, 961888D61A66BF9000295A64 /* DesignableImageView.swift */, 961888D71A66BF9000295A64 /* DesignableLabel.swift */, 961888D81A66BF9000295A64 /* DesignableTextField.swift */, 961888D91A66BF9000295A64 /* DesignableTextView.swift */, 961888DA1A66BF9000295A64 /* DesignableView.swift */, 969775D31A6AD6AC009B4B79 /* DesignableTabBarController.swift */, 961888DB1A66BF9000295A64 /* ImageLoader.swift */, 1A585F3F1A7B9530007EEB7D /* KeyboardLayoutConstraint.swift */, 961888DF1A66BF9000295A64 /* Misc.swift */, 1A9F866C1A83C5640098BE6C /* SoundPlayer.swift */, 1AD08AC61A676D5800160D45 /* Spring.swift */, 961888E01A66BF9000295A64 /* SpringAnimation.swift */, 961888E11A66BF9000295A64 /* SpringButton.swift */, 1AF3F11A1A6776760090E8F9 /* SpringImageView.swift */, 1AF3F11B1A6776760090E8F9 /* SpringLabel.swift */, 1AF3F11C1A6776760090E8F9 /* SpringTextField.swift */, 1AF3F11D1A6776760090E8F9 /* SpringTextView.swift */, 961888E21A66BF9000295A64 /* SpringView.swift */, 961888E31A66BF9000295A64 /* TransitionManager.swift */, 961888E41A66BF9000295A64 /* TransitionZoom.swift */, 961888E51A66BF9000295A64 /* UnwindSegue.swift */, ); path = Spring; sourceTree = SOURCE_ROOT; }; 964117321A5BE90A000E3A5A = { isa = PBXGroup; children = ( 9641173D1A5BE90A000E3A5A /* SpringApp */, 964117531A5BE90A000E3A5A /* SpringAppTests */, 1A4FDA341A6E44780099D309 /* Spring */, 1A4FDA431A6E44780099D309 /* SpringTests */, 9641173C1A5BE90A000E3A5A /* Products */, ); indentWidth = 4; sourceTree = ""; tabWidth = 4; }; 9641173C1A5BE90A000E3A5A /* Products */ = { isa = PBXGroup; children = ( 9641173B1A5BE90A000E3A5A /* SpringApp.app */, 964117501A5BE90A000E3A5A /* SpringAppTests.xctest */, 1A4FDA331A6E44780099D309 /* Spring.framework */, 1A4FDA3D1A6E44780099D309 /* SpringTests.xctest */, ); name = Products; sourceTree = ""; }; 9641173D1A5BE90A000E3A5A /* SpringApp */ = { isa = PBXGroup; children = ( 961888D21A66BF9000295A64 /* Spring */, 964117401A5BE90A000E3A5A /* AppDelegate.swift */, 964117881A5BEC6F000E3A5A /* SpringViewController.swift */, 964117891A5BEC6F000E3A5A /* OptionsViewController.swift */, 9641178A1A5BEC6F000E3A5A /* CodeViewController.swift */, 964117441A5BE90A000E3A5A /* Main.storyboard */, 964117471A5BE90A000E3A5A /* Images.xcassets */, 964117491A5BE90A000E3A5A /* LaunchScreen.xib */, 9641173E1A5BE90A000E3A5A /* Supporting Files */, ); path = SpringApp; sourceTree = ""; }; 9641173E1A5BE90A000E3A5A /* Supporting Files */ = { isa = PBXGroup; children = ( 9641173F1A5BE90A000E3A5A /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; 964117531A5BE90A000E3A5A /* SpringAppTests */ = { isa = PBXGroup; children = ( 964117561A5BE90A000E3A5A /* SpringAppTests.swift */, 964117541A5BE90A000E3A5A /* Supporting Files */, ); path = SpringAppTests; sourceTree = ""; }; 964117541A5BE90A000E3A5A /* Supporting Files */ = { isa = PBXGroup; children = ( 964117551A5BE90A000E3A5A /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 1A4FDA301A6E44780099D309 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 1A4FDA381A6E44780099D309 /* Spring.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 1A4FDA321A6E44780099D309 /* Spring */ = { isa = PBXNativeTarget; buildConfigurationList = 1A4FDA4C1A6E44780099D309 /* Build configuration list for PBXNativeTarget "Spring" */; buildPhases = ( 1A4FDA2E1A6E44780099D309 /* Sources */, 1A4FDA2F1A6E44780099D309 /* Frameworks */, 1A4FDA301A6E44780099D309 /* Headers */, 1A4FDA311A6E44780099D309 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = Spring; productName = Spring; productReference = 1A4FDA331A6E44780099D309 /* Spring.framework */; productType = "com.apple.product-type.framework"; }; 1A4FDA3C1A6E44780099D309 /* SpringTests */ = { isa = PBXNativeTarget; buildConfigurationList = 1A4FDA501A6E44780099D309 /* Build configuration list for PBXNativeTarget "SpringTests" */; buildPhases = ( 1A4FDA391A6E44780099D309 /* Sources */, 1A4FDA3A1A6E44780099D309 /* Frameworks */, 1A4FDA3B1A6E44780099D309 /* Resources */, ); buildRules = ( ); dependencies = ( 1A4FDA401A6E44780099D309 /* PBXTargetDependency */, 1A4FDA421A6E44780099D309 /* PBXTargetDependency */, ); name = SpringTests; productName = SpringTests; productReference = 1A4FDA3D1A6E44780099D309 /* SpringTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; 9641173A1A5BE90A000E3A5A /* SpringApp */ = { isa = PBXNativeTarget; buildConfigurationList = 9641175A1A5BE90A000E3A5A /* Build configuration list for PBXNativeTarget "SpringApp" */; buildPhases = ( 964117371A5BE90A000E3A5A /* Sources */, 964117381A5BE90A000E3A5A /* Frameworks */, 964117391A5BE90A000E3A5A /* Resources */, 1A4FDA4F1A6E44780099D309 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( 1A4FDA491A6E44780099D309 /* PBXTargetDependency */, ); name = SpringApp; productName = SpringApp; productReference = 9641173B1A5BE90A000E3A5A /* SpringApp.app */; productType = "com.apple.product-type.application"; }; 9641174F1A5BE90A000E3A5A /* SpringAppTests */ = { isa = PBXNativeTarget; buildConfigurationList = 9641175D1A5BE90A000E3A5A /* Build configuration list for PBXNativeTarget "SpringAppTests" */; buildPhases = ( 9641174C1A5BE90A000E3A5A /* Sources */, 9641174D1A5BE90A000E3A5A /* Frameworks */, 9641174E1A5BE90A000E3A5A /* Resources */, ); buildRules = ( ); dependencies = ( 964117521A5BE90A000E3A5A /* PBXTargetDependency */, ); name = SpringAppTests; productName = SpringAppTests; productReference = 964117501A5BE90A000E3A5A /* SpringAppTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 964117331A5BE90A000E3A5A /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0700; LastUpgradeCheck = 0900; ORGANIZATIONNAME = "Meng To"; TargetAttributes = { 1A4FDA321A6E44780099D309 = { CreatedOnToolsVersion = 6.1.1; LastSwiftMigration = 1020; }; 1A4FDA3C1A6E44780099D309 = { CreatedOnToolsVersion = 6.1.1; LastSwiftMigration = 1020; TestTargetID = 9641173A1A5BE90A000E3A5A; }; 9641173A1A5BE90A000E3A5A = { CreatedOnToolsVersion = 6.2; LastSwiftMigration = 1020; }; 9641174F1A5BE90A000E3A5A = { CreatedOnToolsVersion = 6.2; LastSwiftMigration = 1020; TestTargetID = 9641173A1A5BE90A000E3A5A; }; }; }; buildConfigurationList = 964117361A5BE90A000E3A5A /* Build configuration list for PBXProject "SpringApp" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( English, en, Base, ); mainGroup = 964117321A5BE90A000E3A5A; productRefGroup = 9641173C1A5BE90A000E3A5A /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 9641173A1A5BE90A000E3A5A /* SpringApp */, 9641174F1A5BE90A000E3A5A /* SpringAppTests */, 1A4FDA321A6E44780099D309 /* Spring */, 1A4FDA3C1A6E44780099D309 /* SpringTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 1A4FDA311A6E44780099D309 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 1AB764641A6E4F070076CD78 /* Images.xcassets in Resources */, 1A4FDA541A6E44A70099D309 /* LoadingView.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 1A4FDA3B1A6E44780099D309 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 964117391A5BE90A000E3A5A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 964117461A5BE90A000E3A5A /* Main.storyboard in Resources */, 9641174B1A5BE90A000E3A5A /* LaunchScreen.xib in Resources */, 964117481A5BE90A000E3A5A /* Images.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 9641174E1A5BE90A000E3A5A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 1A4FDA2E1A6E44780099D309 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 1A4FDA531A6E44A70099D309 /* LoadingView.swift in Sources */, 1A4FDA5B1A6E44A70099D309 /* DesignableTextView.swift in Sources */, 1A4FDA671A6E44A70099D309 /* SpringTextView.swift in Sources */, 1A4FDA641A6E44A70099D309 /* SpringImageView.swift in Sources */, 1A4FDA581A6E44A70099D309 /* DesignableImageView.swift in Sources */, 1A4FDA601A6E44A70099D309 /* Misc.swift in Sources */, 1A4FDA611A6E44A70099D309 /* Spring.swift in Sources */, 1A4FDA5A1A6E44A70099D309 /* DesignableTextField.swift in Sources */, 1A4FDA691A6E44A70099D309 /* TransitionManager.swift in Sources */, 1A4FDA5E1A6E44A70099D309 /* ImageLoader.swift in Sources */, 1A9F866D1A83C5640098BE6C /* SoundPlayer.swift in Sources */, 1A4FDA551A6E44A70099D309 /* BlurView.swift in Sources */, 1AA7E1841AA36EFF00762D75 /* AsyncButton.swift in Sources */, 1A4FDA5D1A6E44A70099D309 /* DesignableTabBarController.swift in Sources */, 1A4FDA591A6E44A70099D309 /* DesignableLabel.swift in Sources */, 1A4FDA661A6E44A70099D309 /* SpringTextField.swift in Sources */, 1A4FDA631A6E44A70099D309 /* SpringButton.swift in Sources */, 1A4FDA621A6E44A70099D309 /* SpringAnimation.swift in Sources */, 1A585F401A7B9530007EEB7D /* KeyboardLayoutConstraint.swift in Sources */, 1AA7E1831AA36EFF00762D75 /* AsyncImageView.swift in Sources */, 1A4FDA6B1A6E44A70099D309 /* UnwindSegue.swift in Sources */, 1A4FDA681A6E44A70099D309 /* SpringView.swift in Sources */, 1A4FDA6A1A6E44A70099D309 /* TransitionZoom.swift in Sources */, 1A4FDA651A6E44A70099D309 /* SpringLabel.swift in Sources */, 1A4FDA571A6E44A70099D309 /* DesignableButton.swift in Sources */, 1A4FDA5C1A6E44A70099D309 /* DesignableView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 1A4FDA391A6E44780099D309 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 1A4FDA471A6E44780099D309 /* SpringTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 964117371A5BE90A000E3A5A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 964117411A5BE90A000E3A5A /* AppDelegate.swift in Sources */, 9641178D1A5BEC6F000E3A5A /* CodeViewController.swift in Sources */, 9641178C1A5BEC6F000E3A5A /* OptionsViewController.swift in Sources */, 9641178B1A5BEC6F000E3A5A /* SpringViewController.swift in Sources */, 96C5F4621AC464C800BB8A18 /* AutoTextView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 9641174C1A5BE90A000E3A5A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 964117571A5BE90A000E3A5A /* SpringAppTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 1A4FDA401A6E44780099D309 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 1A4FDA321A6E44780099D309 /* Spring */; targetProxy = 1A4FDA3F1A6E44780099D309 /* PBXContainerItemProxy */; }; 1A4FDA421A6E44780099D309 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 9641173A1A5BE90A000E3A5A /* SpringApp */; targetProxy = 1A4FDA411A6E44780099D309 /* PBXContainerItemProxy */; }; 1A4FDA491A6E44780099D309 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 1A4FDA321A6E44780099D309 /* Spring */; targetProxy = 1A4FDA481A6E44780099D309 /* PBXContainerItemProxy */; }; 964117521A5BE90A000E3A5A /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 9641173A1A5BE90A000E3A5A /* SpringApp */; targetProxy = 964117511A5BE90A000E3A5A /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ 964117441A5BE90A000E3A5A /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 964117451A5BE90A000E3A5A /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 964117491A5BE90A000E3A5A /* LaunchScreen.xib */ = { isa = PBXVariantGroup; children = ( 9641174A1A5BE90A000E3A5A /* Base */, ); name = LaunchScreen.xib; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 1A4FDA4D1A6E44780099D309 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = Spring/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "designcode.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 1A4FDA4E1A6E44780099D309 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Spring/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "designcode.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 1A4FDA511A6E44780099D309 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = SpringTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.jamztang.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SpringApp.app/SpringApp"; }; name = Debug; }; 1A4FDA521A6E44780099D309 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { INFOPLIST_FILE = SpringTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.jamztang.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SpringApp.app/SpringApp"; }; name = Release; }; 964117581A5BE90A000E3A5A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.2; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; }; name = Debug; }; 964117591A5BE90A000E3A5A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.2; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_SWIFT3_OBJC_INFERENCE = Off; VALIDATE_PRODUCT = YES; }; name = Release; }; 9641175B1A5BE90A000E3A5A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = SpringApp/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "designcode.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 9641175C1A5BE90A000E3A5A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = SpringApp/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "designcode.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; 9641175E1A5BE90A000E3A5A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = SpringAppTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "designcode.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SpringApp.app/SpringApp"; }; name = Debug; }; 9641175F1A5BE90A000E3A5A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; INFOPLIST_FILE = SpringAppTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "designcode.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SpringApp.app/SpringApp"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 1A4FDA4C1A6E44780099D309 /* Build configuration list for PBXNativeTarget "Spring" */ = { isa = XCConfigurationList; buildConfigurations = ( 1A4FDA4D1A6E44780099D309 /* Debug */, 1A4FDA4E1A6E44780099D309 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 1A4FDA501A6E44780099D309 /* Build configuration list for PBXNativeTarget "SpringTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 1A4FDA511A6E44780099D309 /* Debug */, 1A4FDA521A6E44780099D309 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 964117361A5BE90A000E3A5A /* Build configuration list for PBXProject "SpringApp" */ = { isa = XCConfigurationList; buildConfigurations = ( 964117581A5BE90A000E3A5A /* Debug */, 964117591A5BE90A000E3A5A /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 9641175A1A5BE90A000E3A5A /* Build configuration list for PBXNativeTarget "SpringApp" */ = { isa = XCConfigurationList; buildConfigurations = ( 9641175B1A5BE90A000E3A5A /* Debug */, 9641175C1A5BE90A000E3A5A /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 9641175D1A5BE90A000E3A5A /* Build configuration list for PBXNativeTarget "SpringAppTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 9641175E1A5BE90A000E3A5A /* Debug */, 9641175F1A5BE90A000E3A5A /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 964117331A5BE90A000E3A5A /* Project object */; } ================================================ FILE: SpringApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: SpringApp.xcodeproj/xcshareddata/xcschemes/Spring.xcscheme ================================================ ================================================ FILE: SpringApp.xcodeproj/xcshareddata/xcschemes/SpringApp.xcscheme ================================================ ================================================ FILE: SpringAppTests/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: SpringAppTests/SpringAppTests.swift ================================================ // // SpringAppTests.swift // SpringAppTests // // Created by Meng To on 2015-01-06. // Copyright (c) 2015 Meng To. All rights reserved. // import UIKit import XCTest class SpringAppTests: XCTestCase { override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } func testExample() { // This is an example of a functional test case. XCTAssert(true, "Pass") } func testPerformanceExample() { // This is an example of a performance test case. self.measure() { // Put the code you want to measure the time of here. } } } ================================================ FILE: SpringTests/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: SpringTests/SpringTests.swift ================================================ // // SpringTests.swift // SpringTests // // Created by James Tang on 20/1/15. // Copyright (c) 2015 Meng To. All rights reserved. // import UIKit import XCTest class SpringTests: XCTestCase { override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } func testExample() { // This is an example of a functional test case. XCTAssert(true, "Pass") } func testPerformanceExample() { // This is an example of a performance test case. self.measure() { // Put the code you want to measure the time of here. } } }