diff --git a/.swift-version b/.swift-version new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/.swift-version @@ -0,0 +1 @@ +4 diff --git a/README.md b/README.md index 6d8ee91..34f1430 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,14 @@ -![](http://cl.ly/image/3a1r1d3l3D1j/spring-logo.jpg) +## Updated for Swift 4.2 +Requires Xcode 10 and Swift 4.2. ## Installation -Drop in the Spring folder to your Xcode project. +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. @@ -28,7 +35,7 @@ In Identity Inspector, connect the UIView to SpringView Class and set the animat animateTo() animateToNext { ... } -## Presets +## Animation shake pop morph @@ -58,9 +65,9 @@ In Identity Inspector, connect the UIView to SpringView Class and set the animat ## Curve spring - linear - easeIn - easeOut + linear + easeIn + easeOut easeInOut ## Properties @@ -69,6 +76,7 @@ In Identity Inspector, connect the UIView to SpringView Class and set the animat delay damping velocity + repeatCount scale x y @@ -78,7 +86,7 @@ In Identity Inspector, connect the UIView to SpringView Class and set the animat ## Autostart -Allows you to animate without code. Don't need to use this is if you plan to start the animation in code. +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(). @@ -87,9 +95,12 @@ Saves you the hassle of adding a line "layer.alpha = 0" in viewDidLoad(). Animations won't autostart when view is reached via performSegueWithIdentifier. ## Tutorials -Tutorials available on [Design+Code](https://designcode.io/swiftapp). +- 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. - diff --git a/Spring.podspec b/Spring.podspec new file mode 100644 index 0000000..a43ed30 --- /dev/null +++ b/Spring.podspec @@ -0,0 +1,15 @@ +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 diff --git a/Spring/AsyncButton.swift b/Spring/AsyncButton.swift new file mode 100644 index 0000000..6e03ac9 --- /dev/null +++ b/Spring/AsyncButton.swift @@ -0,0 +1,51 @@ +// 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) + } + }) + } + } + } + } + +} diff --git a/Spring/AsyncImageView.swift b/Spring/AsyncImageView.swift new file mode 100644 index 0000000..aa2f7e8 --- /dev/null +++ b/Spring/AsyncImageView.swift @@ -0,0 +1,51 @@ +// 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 + } + +} diff --git a/Spring/AutoTextView.swift b/Spring/AutoTextView.swift new file mode 100644 index 0000000..1d0c7e6 --- /dev/null +++ b/Spring/AutoTextView.swift @@ -0,0 +1,27 @@ +// +// 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 + } + } +} diff --git a/Spring/BlurView.swift b/Spring/BlurView.swift index 4583633..8de1611 100644 --- a/Spring/BlurView.swift +++ b/Spring/BlurView.swift @@ -22,11 +22,12 @@ import UIKit -public func insertBlurView (view: UIView, style: UIBlurEffectStyle) { - view.backgroundColor = UIColor.clearColor() +public func insertBlurView (view: UIView, style: UIBlurEffect.Style) -> UIVisualEffectView { + view.backgroundColor = UIColor.clear - var blurEffect = UIBlurEffect(style: style) - var blurEffectView = UIVisualEffectView(effect: blurEffect) + let blurEffect = UIBlurEffect(style: style) + let blurEffectView = UIVisualEffectView(effect: blurEffect) blurEffectView.frame = view.bounds - view.insertSubview(blurEffectView, atIndex: 0) -} \ No newline at end of file + view.insertSubview(blurEffectView, at: 0) + return blurEffectView +} diff --git a/Spring/DesignableButton.swift b/Spring/DesignableButton.swift index 5b0a9c7..0612083 100644 --- a/Spring/DesignableButton.swift +++ b/Spring/DesignableButton.swift @@ -24,9 +24,9 @@ import UIKit @IBDesignable public class DesignableButton: SpringButton { - @IBInspectable public var borderColor: UIColor = UIColor.clearColor() { + @IBInspectable public var borderColor: UIColor = UIColor.clear { didSet { - layer.borderColor = borderColor.CGColor + layer.borderColor = borderColor.cgColor } } @@ -42,9 +42,9 @@ import UIKit } } - @IBInspectable public var shadowColor: UIColor = UIColor.clearColor() { + @IBInspectable public var shadowColor: UIColor = UIColor.clear { didSet { - layer.shadowColor = shadowColor.CGColor + layer.shadowColor = shadowColor.cgColor } } diff --git a/Spring/DesignableImageView.swift b/Spring/DesignableImageView.swift index 60c5a16..32a90a2 100644 --- a/Spring/DesignableImageView.swift +++ b/Spring/DesignableImageView.swift @@ -24,9 +24,9 @@ import UIKit @IBDesignable public class DesignableImageView: SpringImageView { - @IBInspectable public var borderColor: UIColor = UIColor.clearColor() { + @IBInspectable public var borderColor: UIColor = UIColor.clear { didSet { - layer.borderColor = borderColor.CGColor + layer.borderColor = borderColor.cgColor } } diff --git a/Spring/DesignableLabel.swift b/Spring/DesignableLabel.swift index e982cb4..8d6994b 100644 --- a/Spring/DesignableLabel.swift +++ b/Spring/DesignableLabel.swift @@ -26,15 +26,15 @@ import UIKit @IBInspectable public var lineHeight: CGFloat = 1.5 { didSet { - var font = UIFont(name: self.font.fontName, size: self.font.pointSize) - var text = self.text + let font = UIFont(name: self.font.fontName, size: self.font.pointSize) + guard let text = self.text else { return } - var paragraphStyle = NSMutableParagraphStyle() + let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = lineHeight - var attributedString = NSMutableAttributedString(string: text!) - attributedString.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: NSMakeRange(0, attributedString.length)) - attributedString.addAttribute(NSFontAttributeName, value: font!, range: NSMakeRange(0, attributedString.length)) + 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 } diff --git a/Spring/DesignableTabBarController.swift b/Spring/DesignableTabBarController.swift index 5637f80..6c81095 100644 --- a/Spring/DesignableTabBarController.swift +++ b/Spring/DesignableTabBarController.swift @@ -24,77 +24,78 @@ import UIKit @IBDesignable class DesignableTabBarController: UITabBarController { - @IBInspectable var normalTint: UIColor = UIColor.clearColor() { + @IBInspectable var normalTint: UIColor = UIColor.clear { didSet { UITabBar.appearance().tintColor = normalTint - UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: normalTint], forState: UIControlState.Normal) + UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: normalTint], for: UIControl.State()) } } - @IBInspectable var selectedTint: UIColor = UIColor.clearColor() { + @IBInspectable var selectedTint: UIColor = UIColor.clear { didSet { - UITabBar.appearance().selectedImageTintColor = selectedTint - UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: selectedTint], forState:UIControlState.Selected) + UITabBar.appearance().tintColor = selectedTint + UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: selectedTint], for:UIControl.State.selected) } } @IBInspectable var fontName: String = "" { didSet { - UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: normalTint, NSFontAttributeName: UIFont(name: fontName, size: 11)!], forState: UIControlState.Normal) + 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.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate) + 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.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate) + 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.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate) + 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.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate) + 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.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate) + if let image = fifthSelectedImage { + var tabBarItems = self.tabBar.items as [UITabBarItem]? + tabBarItems?[4].selectedImage = image.withRenderingMode(UIImage.RenderingMode.alwaysTemplate) } } } override func viewDidLoad() { super.viewDidLoad() - - for item in self.tabBar.items as [UITabBarItem] { - if let image = item.image { - item.image = image.imageWithColor(self.normalTint).imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal) + 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) + } } } } @@ -104,19 +105,19 @@ extension UIImage { func imageWithColor(tintColor: UIColor) -> UIImage { UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale) - let context = UIGraphicsGetCurrentContext() as CGContextRef - CGContextTranslateCTM(context, 0, self.size.height) - CGContextScaleCTM(context, 1.0, -1.0); - CGContextSetBlendMode(context, kCGBlendModeNormal) + 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 = CGRectMake(0, 0, self.size.width, self.size.height) as CGRect - CGContextClipToMask(context, rect, self.CGImage) + let rect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height) + context?.clip(to: rect, mask: self.cgImage!) tintColor.setFill() - CGContextFillRect(context, rect) + context!.fill(rect) - let newImage = UIGraphicsGetImageFromCurrentImageContext() as UIImage + let newImage = UIGraphicsGetImageFromCurrentImageContext()! as UIImage UIGraphicsEndImageContext() return newImage } -} \ No newline at end of file +} diff --git a/Spring/DesignableTextField.swift b/Spring/DesignableTextField.swift index 7d70e2b..e334a8e 100644 --- a/Spring/DesignableTextField.swift +++ b/Spring/DesignableTextField.swift @@ -24,9 +24,10 @@ import UIKit @IBDesignable public class DesignableTextField: SpringTextField { - @IBInspectable public var placeholderColor: UIColor = UIColor.clearColor() { + @IBInspectable public var placeholderColor: UIColor = UIColor.clear { didSet { - attributedPlaceholder = NSAttributedString(string: placeholder!, attributes: [NSForegroundColorAttributeName: placeholderColor]) + guard let placeholder = placeholder else { return } + attributedPlaceholder = NSAttributedString(string: placeholder, attributes: [NSAttributedString.Key.foregroundColor: placeholderColor]) layoutSubviews() } @@ -34,37 +35,37 @@ import UIKit @IBInspectable public var sidePadding: CGFloat = 0 { didSet { - var padding = UIView(frame: CGRectMake(0, 0, sidePadding, sidePadding)) + let padding = UIView(frame: CGRect(x: 0, y: 0, width: sidePadding, height: sidePadding)) - leftViewMode = UITextFieldViewMode.Always + leftViewMode = UITextField.ViewMode.always leftView = padding - rightViewMode = UITextFieldViewMode.Always + rightViewMode = UITextField.ViewMode.always rightView = padding } } @IBInspectable public var leftPadding: CGFloat = 0 { didSet { - var padding = UIView(frame: CGRectMake(0, 0, leftPadding, 0)) + let padding = UIView(frame: CGRect(x: 0, y: 0, width: leftPadding, height: 0)) - leftViewMode = UITextFieldViewMode.Always + leftViewMode = UITextField.ViewMode.always leftView = padding } } @IBInspectable public var rightPadding: CGFloat = 0 { didSet { - var padding = UIView(frame: CGRectMake(0, 0, 0, rightPadding)) + let padding = UIView(frame: CGRect(x: 0, y: 0, width: rightPadding, height: 0)) - rightViewMode = UITextFieldViewMode.Always + rightViewMode = UITextField.ViewMode.always rightView = padding } } - @IBInspectable public var borderColor: UIColor = UIColor.clearColor() { + @IBInspectable public var borderColor: UIColor = UIColor.clear { didSet { - layer.borderColor = borderColor.CGColor + layer.borderColor = borderColor.cgColor } } @@ -82,15 +83,15 @@ import UIKit @IBInspectable public var lineHeight: CGFloat = 1.5 { didSet { - var font = UIFont(name: self.font.fontName, size: self.font.pointSize) - var text = self.text + let font = UIFont(name: self.font!.fontName, size: self.font!.pointSize) + guard let text = self.text else { return } - var paragraphStyle = NSMutableParagraphStyle() + let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = lineHeight - var attributedString = NSMutableAttributedString(string: text!) - attributedString.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: NSMakeRange(0, attributedString.length)) - attributedString.addAttribute(NSFontAttributeName, value: font!, range: NSMakeRange(0, attributedString.length)) + 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 } diff --git a/Spring/DesignableTextView.swift b/Spring/DesignableTextView.swift index cec69e6..779298a 100644 --- a/Spring/DesignableTextView.swift +++ b/Spring/DesignableTextView.swift @@ -24,9 +24,9 @@ import UIKit @IBDesignable public class DesignableTextView: SpringTextView { - @IBInspectable public var borderColor: UIColor = UIColor.clearColor() { + @IBInspectable public var borderColor: UIColor = UIColor.clear { didSet { - layer.borderColor = borderColor.CGColor + layer.borderColor = borderColor.cgColor } } @@ -41,21 +41,21 @@ import UIKit layer.cornerRadius = cornerRadius } } - + @IBInspectable public var lineHeight: CGFloat = 1.5 { didSet { - var font = UIFont(name: self.font.fontName, size: self.font.pointSize) - var text = self.text + let font = UIFont(name: self.font!.fontName, size: self.font!.pointSize) + guard let text = self.text else { return } - var paragraphStyle = NSMutableParagraphStyle() + let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = lineHeight - var attributedString = NSMutableAttributedString(string: text!) - attributedString.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: NSMakeRange(0, attributedString.length)) - attributedString.addAttribute(NSFontAttributeName, value: font!, range: NSMakeRange(0, attributedString.length)) + 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 } } - + } diff --git a/Spring/DesignableView.swift b/Spring/DesignableView.swift index 62cca91..a82d23f 100644 --- a/Spring/DesignableView.swift +++ b/Spring/DesignableView.swift @@ -24,9 +24,9 @@ import UIKit @IBDesignable public class DesignableView: SpringView { - @IBInspectable public var borderColor: UIColor = UIColor.clearColor() { + @IBInspectable public var borderColor: UIColor = UIColor.clear { didSet { - layer.borderColor = borderColor.CGColor + layer.borderColor = borderColor.cgColor } } @@ -42,9 +42,9 @@ import UIKit } } - @IBInspectable public var shadowColor: UIColor = UIColor.clearColor() { + @IBInspectable public var shadowColor: UIColor = UIColor.clear { didSet { - layer.shadowColor = shadowColor.CGColor + layer.shadowColor = shadowColor.cgColor } } @@ -65,4 +65,4 @@ import UIKit layer.shadowOffset.height = shadowOffsetY } } -} \ No newline at end of file +} diff --git a/Spring/ImageLoader.swift b/Spring/ImageLoader.swift index fe13699..bcb626d 100755 --- a/Spring/ImageLoader.swift +++ b/Spring/ImageLoader.swift @@ -26,45 +26,61 @@ import Foundation public class ImageLoader { - var cache = NSCache() + var cache = NSCache() public class var sharedLoader : ImageLoader { - struct Static { - static let instance : ImageLoader = ImageLoader() + struct Static { + static let instance : ImageLoader = ImageLoader() } return Static.instance } - public func imageForUrl(urlString: String, completionHandler:(image: UIImage?, url: String) -> ()) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {()in - var data: NSData? = self.cache.objectForKey(urlString) as? NSData + 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) - dispatch_async(dispatch_get_main_queue(), {() in - completionHandler(image: image, url: urlString) + let image = UIImage(data: goodData as Data) + DispatchQueue.main.async(execute: {() in + completionHandler(image, urlString) }) return } - var downloadTask: NSURLSessionDataTask = NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: urlString)!, completionHandler: {(data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in + let downloadTask: URLSessionDataTask = URLSession.shared.dataTask(with: URL(string: urlString)!, completionHandler: { (data, response, error) -> Void in + if (error != nil) { - completionHandler(image: nil, url: urlString) + completionHandler(nil, urlString) return } if data != nil { - let image = UIImage(data: data) - self.cache.setObject(data, forKey: urlString) - dispatch_async(dispatch_get_main_queue(), {() in - completionHandler(image: image, url: urlString) + 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() - }) + + } } -} \ No newline at end of file +} diff --git a/Spring/Info.plist b/Spring/Info.plist index bd4db03..d3de8ee 100644 --- a/Spring/Info.plist +++ b/Spring/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - designcode.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/Spring/KeyboardLayoutConstraint.swift b/Spring/KeyboardLayoutConstraint.swift index b8dfe5e..1b45a56 100644 --- a/Spring/KeyboardLayoutConstraint.swift +++ b/Spring/KeyboardLayoutConstraint.swift @@ -22,74 +22,77 @@ 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 - - NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShowNotification:", name: UIKeyboardWillShowNotification, object: nil) - NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHideNotification:", name: UIKeyboardWillHideNotification, object: nil) + + 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 { - NSNotificationCenter.defaultCenter().removeObserver(self) + NotificationCenter.default.removeObserver(self) } - + // MARK: Notification - - func keyboardWillShowNotification(notification: NSNotification) { + + @objc func keyboardWillShowNotification(_ notification: Notification) { if let userInfo = notification.userInfo { - if let frameValue = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue { - let frame = frameValue.CGRectValue() + if let frameValue = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue { + let frame = frameValue.cgRectValue keyboardVisibleHeight = frame.size.height } - + self.updateConstant() - switch (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber, userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber) { - case let (.Some(duration), .Some(curve)): - - let options = UIViewAnimationOptions(curve.unsignedLongValue) - - UIView.animateWithDuration( - NSTimeInterval(duration.doubleValue), + 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.sharedApplication().keyWindow?.layoutIfNeeded() + UIApplication.shared.keyWindow?.layoutIfNeeded() return }, completion: { finished in }) default: - + break } - + } - + } - - func keyboardWillHideNotification(notification: NSNotification) { + + @objc func keyboardWillHideNotification(_ notification: NSNotification) { keyboardVisibleHeight = 0 self.updateConstant() - + if let userInfo = notification.userInfo { - - switch (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber, userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber) { - case let (.Some(duration), .Some(curve)): - - let options = UIViewAnimationOptions(curve.unsignedLongValue) - - UIView.animateWithDuration( - NSTimeInterval(duration.doubleValue), + + 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.sharedApplication().keyWindow?.layoutIfNeeded() + UIApplication.shared.keyWindow?.layoutIfNeeded() return }, completion: { finished in }) @@ -98,9 +101,10 @@ public class KeyboardLayoutConstraint: NSLayoutConstraint { } } } - + func updateConstant() { self.constant = offset + keyboardVisibleHeight } - + } +#endif diff --git a/Spring/LoadingView.swift b/Spring/LoadingView.swift index 0e013a7..5f62ce8 100644 --- a/Spring/LoadingView.swift +++ b/Spring/LoadingView.swift @@ -22,6 +22,8 @@ import UIKit +#if !os(tvOS) +@available(tvOS, unavailable) public class LoadingView: UIView { @IBOutlet public weak var indicatorView: SpringView! @@ -29,15 +31,16 @@ public class LoadingView: UIView { override public func awakeFromNib() { let animation = CABasicAnimation() animation.keyPath = "transform.rotation.z" - animation.fromValue = degreesToRadians(0) - animation.toValue = degreesToRadians(360) + animation.fromValue = degreesToRadians(degrees: 0) + animation.toValue = degreesToRadians(degrees: 360) animation.duration = 0.9 animation.repeatCount = HUGE - indicatorView.layer.addAnimation(animation, forKey: "") + indicatorView.layer.add(animation, forKey: "") } class func designCodeLoadingView() -> UIView { - return NSBundle(forClass: self).loadNibNamed("LoadingView", owner: self, options: nil)[0] as UIView + + return Bundle(for: self).loadNibNamed("LoadingView", owner: self, options: nil)![0] as! UIView } } @@ -49,7 +52,7 @@ public extension UIView { public func showLoading() { - if let loadingXibView = self.viewWithTag(LoadingViewConstants.Tag) { + if self.viewWithTag(LoadingViewConstants.Tag) != nil { // If loading view is already found in current view hierachy, do nothing return } @@ -60,7 +63,7 @@ public extension UIView { self.addSubview(loadingXibView) loadingXibView.alpha = 0 - spring(0.7, { + SpringAnimation.spring(duration: 0.7, animations: { loadingXibView.alpha = 1 }) } @@ -70,13 +73,14 @@ public extension UIView { if let loadingXibView = self.viewWithTag(LoadingViewConstants.Tag) { loadingXibView.alpha = 1 - springWithCompletion(0.7, { + SpringAnimation.springWithCompletion(duration: 0.7, animations: { loadingXibView.alpha = 0 - loadingXibView.transform = CGAffineTransformMakeScale(3, 3) - }, { (completed) -> Void in + loadingXibView.transform = CGAffineTransform(scaleX: 3, y: 3) + }, completion: { (completed) -> Void in loadingXibView.removeFromSuperview() }) } } } +#endif diff --git a/Spring/Misc.swift b/Spring/Misc.swift index 2e2f1ae..96024b8 100644 --- a/Spring/Misc.swift +++ b/Spring/Misc.swift @@ -23,32 +23,37 @@ import UIKit public extension String { - public var length: Int { return countElements(self) } + var length: Int { return self.count } + + func toURL() -> NSURL? { + return NSURL(string: self) + } } public func htmlToAttributedString(text: String) -> NSAttributedString! { - let htmlData = text.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) - let htmlString = NSAttributedString(data: htmlData!, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil, error: nil) + 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(M_PI / 180) + return degrees * CGFloat(CGFloat.pi / 180) } -public func delay(delay:Double, closure:()->()) { - dispatch_after( - dispatch_time( - DISPATCH_TIME_NOW, - Int64(delay * Double(NSEC_PER_SEC)) - ), - dispatch_get_main_queue(), closure) +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 = NSURL(string: URL) - let data = NSData(contentsOfURL: url!) +public func imageFromURL(_ Url: String) -> UIImage { + let url = Foundation.URL(string: Url) + let data = try? Data(contentsOf: url!) return UIImage(data: data!)! } @@ -58,40 +63,40 @@ public extension UIColor { var green: CGFloat = 0.0 var blue: CGFloat = 0.0 var alpha: CGFloat = 1.0 + var hex: String = hex if hex.hasPrefix("#") { - let index = advance(hex.startIndex, 1) - let hex = hex.substringFromIndex(index) - let scanner = NSScanner(string: hex) - var hexValue: CUnsignedLongLong = 0 - if scanner.scanHexLongLong(&hexValue) { - switch (countElements(hex)) { - 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") - } - } else { - println("Scan hex error") + 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("Invalid RGB string, missing '#' as prefix") + print("Scan hex error") } self.init(red:red, green:green, blue:blue, alpha:alpha) } @@ -112,92 +117,147 @@ public func UIColorFromRGB(rgbValue: UInt) -> UIColor { } public func stringFromDate(date: NSDate, format: String) -> String { - var dateFormatter = NSDateFormatter() + let dateFormatter = DateFormatter() dateFormatter.dateFormat = format - return dateFormatter.stringFromDate(date) + return dateFormatter.string(from: date as Date) } -public func dateFromString(date: String, format: String) -> NSDate { - var dateFormatter = NSDateFormatter() +public func dateFromString(date: String, format: String) -> Date { + let dateFormatter = DateFormatter() dateFormatter.dateFormat = format - return dateFormatter.dateFromString(date)! + if let date = dateFormatter.date(from: date) { + return date + } else { + return Date(timeIntervalSince1970: 0) + } } public func randomStringWithLength (len : Int) -> NSString { let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - var randomString : NSMutableString = NSMutableString(capacity: len) + let randomString : NSMutableString = NSMutableString(capacity: len) - for (var i=0; i < len; i++){ - var length = UInt32 (letters.length) - var rand = arc4random_uniform(length) - randomString.appendFormat("%C", letters.characterAtIndex(Int(rand))) + 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:NSDate, numericDates:Bool) -> String { - let calendar = NSCalendar.currentCalendar() - let unitFlags = NSCalendarUnit.CalendarUnitMinute | NSCalendarUnit.CalendarUnitHour | NSCalendarUnit.CalendarUnitDay | NSCalendarUnit.CalendarUnitWeekOfYear | NSCalendarUnit.CalendarUnitMonth | NSCalendarUnit.CalendarUnitYear | NSCalendarUnit.CalendarUnitSecond - let now = NSDate() - let earliest = now.earlierDate(date) - let latest = (earliest == now) ? date : now - let components:NSDateComponents = calendar.components(unitFlags, fromDate: earliest, toDate: latest, options: nil) +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) - if (components.year >= 2) { - return "\(components.year)y" - } else if (components.year >= 1){ + 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 (components.month >= 2) { - return "\(components.month)" - } else if (components.month >= 1){ + } else if (month >= 2) { + return "\(month * 4)w" + } else if (month >= 1) { if (numericDates){ - return "1M" + return "4w" } else { - return "1M" + return "4w" } - } else if (components.weekOfYear >= 2) { - return "\(components.weekOfYear)w" - } else if (components.weekOfYear >= 1){ + } else if (weekOfYear >= 2) { + return "\(weekOfYear)w" + } else if (weekOfYear >= 1){ if (numericDates){ return "1w" } else { return "1w" } - } else if (components.day >= 2) { - return "\(components.day)d" - } else if (components.day >= 1){ + } else if (day >= 2) { + return "\(components.day ?? 2)d" + } else if (day >= 1){ if (numericDates){ return "1d" } else { return "1d" } - } else if (components.hour >= 2) { - return "\(components.hour)h" - } else if (components.hour >= 1){ + } else if (hour >= 2) { + return "\(hour)h" + } else if (hour >= 1){ if (numericDates){ return "1h" } else { return "1h" } - } else if (components.minute >= 2) { - return "\(components.minute)m" - } else if (components.minute >= 1){ + } else if (minute >= 2) { + return "\(minute)m" + } else if (minute >= 1){ if (numericDates){ return "1m" } else { return "1m" } - } else if (components.second >= 3) { - return "\(components.second)s" + } else if (second >= 3) { + return "\(second)s" } else { return "now" } -} \ No newline at end of file +} + +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) + } +} diff --git a/Spring/SoundPlayer.swift b/Spring/SoundPlayer.swift new file mode 100644 index 0000000..d2451d9 --- /dev/null +++ b/Spring/SoundPlayer.swift @@ -0,0 +1,60 @@ +// 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) + } +} diff --git a/Spring/Spring.swift b/Spring/Spring.swift index 32926bb..cf86e1a 100644 --- a/Spring/Spring.swift +++ b/Spring/Spring.swift @@ -31,6 +31,7 @@ import UIKit 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 } @@ -39,21 +40,46 @@ import UIKit 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 var view : Springable - + + 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 }} @@ -62,6 +88,7 @@ public class Spring : NSObject { 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 }} @@ -70,270 +97,346 @@ public class Spring : NSObject { 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() { - if animation == "" { - return - } - - 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) - animation.duration = CFTimeInterval(duration) - animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) - animation.autoreverses = true - layer.addAnimation(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(M_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) - animation.duration = CFTimeInterval(duration) - animation.additive = true - animation.repeatCount = 1 - animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) - layer.addAnimation(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) - animation.duration = CFTimeInterval(duration) - animation.additive = true - animation.repeatCount = 1 - animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) - layer.addAnimation(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(M_PI), 0, 1, 0))) - animation.duration = CFTimeInterval(duration) - animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) - animation.timingFunction = getTimingFunction(curve) - layer.addAnimation(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(M_PI), 1, 0, 0))) - animation.duration = CFTimeInterval(duration) - animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) - animation.timingFunction = getTimingFunction(curve) - layer.addAnimation(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) - morphX.duration = CFTimeInterval(duration) - morphX.repeatCount = 1 - morphX.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) - layer.addAnimation(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) - morphY.duration = CFTimeInterval(duration) - morphY.repeatCount = 1 - morphY.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) - layer.addAnimation(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) - morphX.duration = CFTimeInterval(duration) - morphX.repeatCount = 1 - morphX.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) - layer.addAnimation(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) - morphY.duration = CFTimeInterval(duration) - morphY.repeatCount = 1 - morphY.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) - layer.addAnimation(morphY, forKey: "morphY") - case "flash": - let animation = CABasicAnimation() - animation.keyPath = "opacity" - animation.fromValue = 1 - animation.toValue = 0 - animation.duration = CFTimeInterval(duration) - animation.repeatCount = 2 - animation.autoreverses = true - animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) - layer.addAnimation(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.additive = true - animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) - layer.addAnimation(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) - x.duration = CFTimeInterval(duration) - x.additive = true - x.repeatCount = 1 - x.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) - layer.addAnimation(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.additive = true - animation.beginTime = CACurrentMediaTime() + CFTimeInterval(delay) - layer.addAnimation(animation, forKey: "swing") - default: - x = 300 + 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 { - switch curve { - case "easeIn": - return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn) - case "easeOut": - return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) - case "easeInOut": - return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) - case "linear": - return CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear) - case "spring": - return CAMediaTimingFunction(controlPoints: 0.5, 1.1+Float(force/3), 1, 1) - default: - return CAMediaTimingFunction(name: kCAMediaTimingFunctionDefault) + 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) -> UIViewAnimationOptions { - switch curve { - case "easeIn": - return UIViewAnimationOptions.CurveEaseIn - case "easeOut": - return UIViewAnimationOptions.CurveEaseOut - case "easeInOut": - return UIViewAnimationOptions.CurveEaseInOut - case "linear": - return UIViewAnimationOptions.CurveLinear - case "spring": - return UIViewAnimationOptions.CurveLinear - default: - return UIViewAnimationOptions.CurveLinear + + 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: () -> ()) { + + public func animateNext(completion: @escaping () -> ()) { animateFrom = true animatePreset() setView { completion() } } - + public func animateTo() { animateFrom = false animatePreset() setView {} } - - public func animateToNext(completion: () -> ()) { + + public func animateToNext(completion: @escaping () -> ()) { animateFrom = false animatePreset() setView { @@ -347,61 +450,70 @@ public class Spring : NSObject { } } - public func customDidMoveToWindow() { - if autostart { - alpha = 0 - animateFrom = true - animatePreset() - setView {} + public func customLayoutSubviews() { + if shouldAnimateInLayoutSubviews { + shouldAnimateInLayoutSubviews = false + if autostart { + if UIApplication.shared.applicationState != .active { + shouldAnimateAfterActive = true + return + } + alpha = 0 + animate() + } } } - - func setView(completion: () -> ()) { + + func setView(completion: @escaping () -> ()) { if animateFrom { - let translate = CGAffineTransformMakeTranslation(self.x, self.y) - let scale = CGAffineTransformMakeScale(self.scaleX, self.scaleY) - let rotate = CGAffineTransformMakeRotation(self.rotate) - let translateAndScale = CGAffineTransformConcat(translate, scale) - self.transform = CGAffineTransformConcat(rotate, translateAndScale) - + 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.animateWithDuration( NSTimeInterval(duration), - delay: NSTimeInterval(delay), - usingSpringWithDamping: damping, - initialSpringVelocity: velocity, - options: getAnimationOptions(curve), - animations: { - - if self.animateFrom { - self.transform = CGAffineTransformIdentity - self.alpha = 1 - } - else { - let translate = CGAffineTransformMakeTranslation(self.x, self.y) - let scale = CGAffineTransformMakeScale(self.scaleX, self.scaleY) - let rotate = CGAffineTransformMakeRotation(self.rotate) - let translateAndScale = CGAffineTransformConcat(translate, scale) - self.transform = CGAffineTransformConcat(rotate, translateAndScale) - - self.alpha = self.opacity - } - - }, { finished in - + + 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() - + self?.resetAll() + }) + } - + func reset() { x = 0 y = 0 opacity = 1 } - + func resetAll() { x = 0 y = 0 @@ -412,8 +524,9 @@ public class Spring : NSObject { rotate = 0 damping = 0.7 velocity = 0.7 + repeatCount = 1 delay = 0 duration = 0.7 } - -} \ No newline at end of file + +} diff --git a/Spring/SpringAnimation.swift b/Spring/SpringAnimation.swift index ff5d787..998882b 100644 --- a/Spring/SpringAnimation.swift +++ b/Spring/SpringAnimation.swift @@ -22,106 +22,91 @@ import UIKit -public func spring(duration: NSTimeInterval, animations: (() -> Void)!) { - - UIView.animateWithDuration( - duration, - delay: 0, - usingSpringWithDamping: 0.7, - initialSpringVelocity: 0.7, - options: nil, - animations: { - - animations() - - }, completion: { finished in - }) -} - -public func springEaseIn(duration: NSTimeInterval, animations: (() -> Void)!) { - - UIView.animateWithDuration( - duration, - delay: 0, - options: UIViewAnimationOptions.CurveEaseIn, - animations: { - - animations() - - }, completion: { finished in - }) -} +@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 func springEaseOut(duration: NSTimeInterval, animations: (() -> Void)!) { - - UIView.animateWithDuration( - duration, - delay: 0, - options: UIViewAnimationOptions.CurveEaseOut, - animations: { - - animations() - - }, completion: { finished in - }) -} + public class func springEaseIn(duration: TimeInterval, animations: (() -> Void)!) { + UIView.animate( + withDuration: duration, + delay: 0, + options: .curveEaseIn, + animations: { + animations() + }, + completion: nil + ) + } -public func springEaseInOut(duration: NSTimeInterval, animations: (() -> Void)!) { - - UIView.animateWithDuration( - duration, - delay: 0, - options: UIViewAnimationOptions.CurveEaseInOut, - animations: { - - animations() - - }, completion: { finished in - }) -} + public class func springEaseOut(duration: TimeInterval, animations: (() -> Void)!) { + UIView.animate( + withDuration: duration, + delay: 0, + options: .curveEaseOut, + animations: { + animations() + }, completion: nil + ) + } -public func springLinear(duration: NSTimeInterval, animations: (() -> Void)!) { - - UIView.animateWithDuration( - duration, - delay: 0, - options: UIViewAnimationOptions.CurveLinear, - animations: { - - animations() - - }, completion: { finished in - }) -} + public class func springEaseInOut(duration: TimeInterval, animations: (() -> Void)!) { + UIView.animate( + withDuration: duration, + delay: 0, + options: UIView.AnimationOptions(), + animations: { + animations() + }, completion: nil + ) + } -public func springWithDelay(duration: NSTimeInterval, delay: NSTimeInterval, animations: (() -> Void)!) { - UIView.animateWithDuration( - duration, - delay: delay, - usingSpringWithDamping: 0.7, - initialSpringVelocity: 0.7, - options: nil, - animations: { - - animations() - - }, completion: { finished in - }) -} + public class func springLinear(duration: TimeInterval, animations: (() -> Void)!) { + UIView.animate( + withDuration: duration, + delay: 0, + options: .curveLinear, + animations: { + animations() + }, completion: nil + ) + } -public func springWithCompletion(duration: NSTimeInterval, animations: (() -> Void)!, completion: ((Bool) -> Void)!) { + 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 + ) + } - UIView.animateWithDuration( - duration, - delay: 0, - usingSpringWithDamping: 0.7, - initialSpringVelocity: 0.7, - options: nil, - animations: { - - animations() - - }, completion: { finished in - completion(true) - }) -} \ No newline at end of file + 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) + } + ) + } +} diff --git a/Spring/SpringButton.swift b/Spring/SpringButton.swift index ddbfc8f..92124ea 100644 --- a/Spring/SpringButton.swift +++ b/Spring/SpringButton.swift @@ -22,7 +22,7 @@ import UIKit -public class SpringButton: UIButton, Springable { +open class SpringButton: UIButton, Springable { @IBInspectable public var autostart: Bool = false @IBInspectable public var autohide: Bool = false @IBInspectable public var animation: String = "" @@ -31,6 +31,7 @@ public class SpringButton: UIButton, Springable { @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 @@ -42,29 +43,29 @@ public class SpringButton: UIButton, Springable { lazy private var spring : Spring = Spring(self) - override public func awakeFromNib() { + override open func awakeFromNib() { super.awakeFromNib() self.spring.customAwakeFromNib() } - override public func didMoveToWindow() { - super.didMoveToWindow() - self.spring.customDidMoveToWindow() + open override func layoutSubviews() { + super.layoutSubviews() + spring.customLayoutSubviews() } public func animate() { self.spring.animate() } - public func animateNext(completion: () -> ()) { - self.spring.animateNext(completion) + public func animateNext(completion: @escaping () -> ()) { + self.spring.animateNext(completion: completion) } public func animateTo() { self.spring.animateTo() } - public func animateToNext(completion: () -> ()) { - self.spring.animateToNext(completion) + public func animateToNext(completion: @escaping () -> ()) { + self.spring.animateToNext(completion: completion) } -} \ No newline at end of file +} diff --git a/Spring/SpringImageView.swift b/Spring/SpringImageView.swift index ac65cbf..85ad61f 100644 --- a/Spring/SpringImageView.swift +++ b/Spring/SpringImageView.swift @@ -22,7 +22,7 @@ import UIKit -public class SpringImageView: UIImageView, Springable { +open class SpringImageView: UIImageView, Springable { @IBInspectable public var autostart: Bool = false @IBInspectable public var autohide: Bool = false @IBInspectable public var animation: String = "" @@ -31,6 +31,7 @@ public class SpringImageView: UIImageView, Springable { @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 @@ -42,30 +43,30 @@ public class SpringImageView: UIImageView, Springable { lazy private var spring : Spring = Spring(self) - override public func awakeFromNib() { + override open func awakeFromNib() { super.awakeFromNib() self.spring.customAwakeFromNib() } - override public func didMoveToWindow() { - super.didMoveToWindow() - self.spring.customDidMoveToWindow() + open override func layoutSubviews() { + super.layoutSubviews() + spring.customLayoutSubviews() } public func animate() { self.spring.animate() } - public func animateNext(completion: () -> ()) { - self.spring.animateNext(completion) + public func animateNext(completion: @escaping () -> ()) { + self.spring.animateNext(completion: completion) } public func animateTo() { self.spring.animateTo() } - public func animateToNext(completion: () -> ()) { - self.spring.animateToNext(completion) + public func animateToNext(completion: @escaping () -> ()) { + self.spring.animateToNext(completion: completion) } -} \ No newline at end of file +} diff --git a/Spring/SpringLabel.swift b/Spring/SpringLabel.swift index 3adc0ef..c105a48 100644 --- a/Spring/SpringLabel.swift +++ b/Spring/SpringLabel.swift @@ -22,7 +22,7 @@ import UIKit -public class SpringLabel: UILabel, Springable { +open class SpringLabel: UILabel, Springable { @IBInspectable public var autostart: Bool = false @IBInspectable public var autohide: Bool = false @IBInspectable public var animation: String = "" @@ -31,6 +31,7 @@ public class SpringLabel: UILabel, Springable { @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 @@ -42,30 +43,30 @@ public class SpringLabel: UILabel, Springable { lazy private var spring : Spring = Spring(self) - override public func awakeFromNib() { + override open func awakeFromNib() { super.awakeFromNib() self.spring.customAwakeFromNib() } - override public func didMoveToWindow() { - super.didMoveToWindow() - self.spring.customDidMoveToWindow() + open override func layoutSubviews() { + super.layoutSubviews() + spring.customLayoutSubviews() } public func animate() { self.spring.animate() } - public func animateNext(completion: () -> ()) { - self.spring.animateNext(completion) + public func animateNext(completion: @escaping () -> ()) { + self.spring.animateNext(completion: completion) } public func animateTo() { self.spring.animateTo() } - public func animateToNext(completion: () -> ()) { - self.spring.animateToNext(completion) + public func animateToNext(completion: @escaping () -> ()) { + self.spring.animateToNext(completion: completion) } -} \ No newline at end of file +} diff --git a/Spring/SpringTextField.swift b/Spring/SpringTextField.swift index a5b389e..966b6f7 100644 --- a/Spring/SpringTextField.swift +++ b/Spring/SpringTextField.swift @@ -22,7 +22,7 @@ import UIKit -public class SpringTextField: UITextField, Springable { +open class SpringTextField: UITextField, Springable { @IBInspectable public var autostart: Bool = false @IBInspectable public var autohide: Bool = false @IBInspectable public var animation: String = "" @@ -31,6 +31,7 @@ public class SpringTextField: UITextField, Springable { @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 @@ -42,29 +43,29 @@ public class SpringTextField: UITextField, Springable { lazy private var spring : Spring = Spring(self) - override public func awakeFromNib() { + override open func awakeFromNib() { super.awakeFromNib() self.spring.customAwakeFromNib() } - override public func didMoveToWindow() { - super.didMoveToWindow() - self.spring.customDidMoveToWindow() + open override func layoutSubviews() { + super.layoutSubviews() + spring.customLayoutSubviews() } public func animate() { self.spring.animate() } - public func animateNext(completion: () -> ()) { - self.spring.animateNext(completion) + public func animateNext(completion: @escaping () -> ()) { + self.spring.animateNext(completion: completion) } public func animateTo() { self.spring.animateTo() } - public func animateToNext(completion: () -> ()) { - self.spring.animateToNext(completion) + public func animateToNext(completion: @escaping () -> ()) { + self.spring.animateToNext(completion: completion) } -} \ No newline at end of file +} diff --git a/Spring/SpringTextView.swift b/Spring/SpringTextView.swift index 22184c0..55d4e4a 100644 --- a/Spring/SpringTextView.swift +++ b/Spring/SpringTextView.swift @@ -22,7 +22,7 @@ import UIKit -public class SpringTextView: UITextView, Springable { +open class SpringTextView: UITextView, Springable { @IBInspectable public var autostart: Bool = false @IBInspectable public var autohide: Bool = false @IBInspectable public var animation: String = "" @@ -31,6 +31,7 @@ public class SpringTextView: UITextView, Springable { @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 @@ -42,30 +43,30 @@ public class SpringTextView: UITextView, Springable { lazy private var spring : Spring = Spring(self) - override public func awakeFromNib() { + override open func awakeFromNib() { super.awakeFromNib() self.spring.customAwakeFromNib() } - override public func didMoveToWindow() { - super.didMoveToWindow() - self.spring.customDidMoveToWindow() + open override func layoutSubviews() { + super.layoutSubviews() + spring.customLayoutSubviews() } public func animate() { self.spring.animate() } - public func animateNext(completion: () -> ()) { - self.spring.animateNext(completion) + public func animateNext(completion: @escaping () -> ()) { + self.spring.animateNext(completion: completion) } public func animateTo() { self.spring.animateTo() } - public func animateToNext(completion: () -> ()) { - self.spring.animateToNext(completion) + public func animateToNext(completion: @escaping () -> ()) { + self.spring.animateToNext(completion: completion) } -} \ No newline at end of file +} diff --git a/Spring/SpringView.swift b/Spring/SpringView.swift index f495905..a00c12a 100644 --- a/Spring/SpringView.swift +++ b/Spring/SpringView.swift @@ -22,7 +22,7 @@ import UIKit -public class SpringView: UIView, Springable { +open class SpringView: UIView, Springable { @IBInspectable public var autostart: Bool = false @IBInspectable public var autohide: Bool = false @IBInspectable public var animation: String = "" @@ -31,6 +31,7 @@ public class SpringView: UIView, Springable { @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 @@ -42,29 +43,29 @@ public class SpringView: UIView, Springable { lazy private var spring : Spring = Spring(self) - override public func awakeFromNib() { + override open func awakeFromNib() { super.awakeFromNib() self.spring.customAwakeFromNib() } - - override public func didMoveToWindow() { - super.didMoveToWindow() - self.spring.customDidMoveToWindow() + + open override func layoutSubviews() { + super.layoutSubviews() + spring.customLayoutSubviews() } public func animate() { self.spring.animate() } - public func animateNext(completion: () -> ()) { - self.spring.animateNext(completion) + public func animateNext(completion: @escaping () -> ()) { + self.spring.animateNext(completion: completion) } public func animateTo() { self.spring.animateTo() } - public func animateToNext(completion: () -> ()) { - self.spring.animateToNext(completion) + public func animateToNext(completion: @escaping () -> ()) { + self.spring.animateToNext(completion: completion) } -} \ No newline at end of file +} diff --git a/Spring/TransitionManager.swift b/Spring/TransitionManager.swift index 3167a57..388edac 100644 --- a/Spring/TransitionManager.swift +++ b/Spring/TransitionManager.swift @@ -27,48 +27,59 @@ public class TransitionManager: NSObject, UIViewControllerTransitioningDelegate, var isPresenting = true var duration = 0.3 - public func animateTransition(transitionContext: UIViewControllerContextTransitioning) { - let container = transitionContext.containerView() - let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)! - let toView = transitionContext.viewForKey(UITransitionContextToViewKey)! + 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) - - toView.transform = CGAffineTransformMakeTranslation(0, toView.frame.size.height) - springEaseInOut(duration) { - fromView.transform = CGAffineTransformMakeScale(0.8, 0.8) + SpringAnimation.springEaseInOut(duration: duration) { + fromView.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) fromView.alpha = 0.5 - toView.transform = CGAffineTransformIdentity + 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) - - springEaseInOut(duration) { - fromView.transform = CGAffineTransformMakeTranslation(0, fromView.frame.size.height) - toView.transform = CGAffineTransformMakeScale(1, 1) + + SpringAnimation.springEaseInOut(duration: duration) { + fromView.transform = CGAffineTransform(translationX: 0, y: fromView.frame.size.height) + toView.transform = CGAffineTransform.identity toView.alpha = 1 } } - delay(duration, { + delay(delay: duration, closure: { transitionContext.completeTransition(true) }) } - public func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval { + public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return duration } - public func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { + public func animationController(forPresentedController presented: UIViewController, presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { isPresenting = true return self } - public func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { + public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { isPresenting = false return self } diff --git a/Spring/TransitionZoom.swift b/Spring/TransitionZoom.swift index da1f4bc..6cf5301 100644 --- a/Spring/TransitionZoom.swift +++ b/Spring/TransitionZoom.swift @@ -27,21 +27,22 @@ public class TransitionZoom: NSObject, UIViewControllerTransitioningDelegate, UI var isPresenting = true var duration = 0.4 - public func animateTransition(transitionContext: UIViewControllerContextTransitioning) { - let container = transitionContext.containerView() - let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)! - let toView = transitionContext.viewForKey(UITransitionContextToViewKey)! + 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 = CGAffineTransformMakeScale(2, 2) - springEaseInOut(duration) { - fromView.transform = CGAffineTransformMakeScale(0.5, 0.5) + 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 = CGAffineTransformIdentity + toView.transform = CGAffineTransform.identity toView.alpha = 1 } } @@ -49,30 +50,30 @@ public class TransitionZoom: NSObject, UIViewControllerTransitioningDelegate, UI container.addSubview(toView) container.addSubview(fromView) - springEaseInOut(duration) { - fromView.transform = CGAffineTransformMakeScale(2, 2) + SpringAnimation.springEaseInOut(duration: duration) { + fromView.transform = CGAffineTransform(scaleX: 2, y: 2) fromView.alpha = 0 - toView.transform = CGAffineTransformMakeScale(1, 1) + toView.transform = CGAffineTransform(scaleX: 1, y: 1) toView.alpha = 1 } } - delay(duration, { + delay(delay: duration, closure: { transitionContext.completeTransition(true) }) } - public func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval { + public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return duration } - public func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { + public func animationController(forPresentedController presented: UIViewController, presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { isPresenting = true return self } - public func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { + public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { isPresenting = false return self } -} \ No newline at end of file +} diff --git a/Spring/UnwindSegue.swift b/Spring/UnwindSegue.swift index a9aae71..53a71cb 100644 --- a/Spring/UnwindSegue.swift +++ b/Spring/UnwindSegue.swift @@ -23,5 +23,5 @@ import UIKit public extension UIViewController { - @IBAction public func unwindToViewController (sender: UIStoryboardSegue){} -} \ No newline at end of file + @IBAction public func unwindToViewController (_ segue: UIStoryboardSegue){} +} diff --git a/SpringApp.xcodeproj/project.pbxproj b/SpringApp.xcodeproj/project.pbxproj index d920c5d..a0e8956 100644 --- a/SpringApp.xcodeproj/project.pbxproj +++ b/SpringApp.xcodeproj/project.pbxproj @@ -15,7 +15,6 @@ 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 */; }; - 1A4FDA561A6E44A70099D309 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888D41A66BF9000295A64 /* Data.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 */; }; @@ -24,7 +23,6 @@ 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 */; }; - 1A4FDA5F1A6E44A70099D309 /* KeyboardAvoidingScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961888DC1A66BF9000295A64 /* KeyboardAvoidingScrollView.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 */; }; @@ -38,6 +36,9 @@ 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 */; }; @@ -47,6 +48,7 @@ 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 */ @@ -104,13 +106,15 @@ 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 = ""; }; - 961888D41A66BF9000295A64 /* Data.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Data.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 = ""; }; @@ -118,7 +122,6 @@ 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 = ""; }; - 961888DC1A66BF9000295A64 /* KeyboardAvoidingScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardAvoidingScrollView.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 = ""; }; @@ -139,6 +142,7 @@ 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 */ @@ -212,10 +216,12 @@ 961888D21A66BF9000295A64 /* Spring */ = { isa = PBXGroup; children = ( + 1AA7E1821AA36EFF00762D75 /* AsyncButton.swift */, + 1AA7E1811AA36EFF00762D75 /* AsyncImageView.swift */, + 96C5F4611AC464C800BB8A18 /* AutoTextView.swift */, 1A4FDA2A1A6E44270099D309 /* LoadingView.swift */, 1A4FDA2B1A6E44270099D309 /* LoadingView.xib */, 961888D31A66BF9000295A64 /* BlurView.swift */, - 961888D41A66BF9000295A64 /* Data.swift */, 961888D51A66BF9000295A64 /* DesignableButton.swift */, 961888D61A66BF9000295A64 /* DesignableImageView.swift */, 961888D71A66BF9000295A64 /* DesignableLabel.swift */, @@ -224,9 +230,9 @@ 961888DA1A66BF9000295A64 /* DesignableView.swift */, 969775D31A6AD6AC009B4B79 /* DesignableTabBarController.swift */, 961888DB1A66BF9000295A64 /* ImageLoader.swift */, - 961888DC1A66BF9000295A64 /* KeyboardAvoidingScrollView.swift */, 1A585F3F1A7B9530007EEB7D /* KeyboardLayoutConstraint.swift */, 961888DF1A66BF9000295A64 /* Misc.swift */, + 1A9F866C1A83C5640098BE6C /* SoundPlayer.swift */, 1AD08AC61A676D5800160D45 /* Spring.swift */, 961888E01A66BF9000295A64 /* SpringAnimation.swift */, 961888E11A66BF9000295A64 /* SpringButton.swift */, @@ -251,7 +257,9 @@ 1A4FDA431A6E44780099D309 /* SpringTests */, 9641173C1A5BE90A000E3A5A /* Products */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; }; 9641173C1A5BE90A000E3A5A /* Products */ = { isa = PBXGroup; @@ -399,21 +407,26 @@ 964117331A5BE90A000E3A5A /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0620; + 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; }; }; @@ -423,6 +436,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -484,21 +498,22 @@ 1A4FDA5B1A6E44A70099D309 /* DesignableTextView.swift in Sources */, 1A4FDA671A6E44A70099D309 /* SpringTextView.swift in Sources */, 1A4FDA641A6E44A70099D309 /* SpringImageView.swift in Sources */, - 1A4FDA5F1A6E44A70099D309 /* KeyboardAvoidingScrollView.swift in Sources */, 1A4FDA581A6E44A70099D309 /* DesignableImageView.swift in Sources */, - 1A4FDA561A6E44A70099D309 /* Data.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 */, @@ -524,6 +539,7 @@ 9641178D1A5BEC6F000E3A5A /* CodeViewController.swift in Sources */, 9641178C1A5BEC6F000E3A5A /* OptionsViewController.swift in Sources */, 9641178B1A5BEC6F000E3A5A /* SpringViewController.swift in Sources */, + 96C5F4621AC464C800BB8A18 /* AutoTextView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -583,6 +599,7 @@ 1A4FDA4D1A6E44780099D309 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -594,10 +611,12 @@ ); INFOPLIST_FILE = Spring/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; + 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 = ""; @@ -607,6 +626,7 @@ 1A4FDA4E1A6E44780099D309 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -614,10 +634,13 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Spring/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; + 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 = ""; @@ -627,10 +650,6 @@ 1A4FDA511A6E44780099D309 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", @@ -638,7 +657,9 @@ 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; @@ -646,14 +667,13 @@ 1A4FDA521A6E44780099D309 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(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_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SpringApp.app/SpringApp"; }; name = Release; @@ -666,20 +686,30 @@ 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", @@ -697,6 +727,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; }; name = Debug; }; @@ -708,13 +739,21 @@ 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"; @@ -722,6 +761,7 @@ 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; @@ -731,6 +771,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.2; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; VALIDATE_PRODUCT = YES; }; name = Release; @@ -742,7 +783,9 @@ 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; @@ -754,7 +797,10 @@ 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; @@ -763,17 +809,15 @@ isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); 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; @@ -782,13 +826,12 @@ isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(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_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SpringApp.app/SpringApp"; }; name = Release; diff --git a/SpringApp.xcodeproj/project.xcworkspace/xcshareddata/SpringApp.xccheckout b/SpringApp.xcodeproj/project.xcworkspace/xcshareddata/SpringApp.xccheckout index 272c9d7..7c00884 100644 --- a/SpringApp.xcodeproj/project.xcworkspace/xcshareddata/SpringApp.xccheckout +++ b/SpringApp.xcodeproj/project.xcworkspace/xcshareddata/SpringApp.xccheckout @@ -5,29 +5,29 @@ IDESourceControlProjectFavoriteDictionaryKey IDESourceControlProjectIdentifier - A5CDD406-2D04-44CC-8B76-9B40C5FCC0AA + 90BE4A8A-3AD7-483D-B9EA-ED8463875CE6 IDESourceControlProjectName SpringApp IDESourceControlProjectOriginsDictionary 71F5D8D885CFF6BD7151066D7BFF25CC48A5235E - https://github.com/MengTo/Spring.git + github.com:MengTo/Spring.git IDESourceControlProjectPath SpringApp.xcodeproj IDESourceControlProjectRelativeInstallPathDictionary + + 71F5D8D885CFF6BD7151066D7BFF25CC48A5235E + ../.. + + IDESourceControlProjectURL + github.com:MengTo/Spring.git IDESourceControlProjectVersion 111 + IDESourceControlProjectWCCIdentifier + 71F5D8D885CFF6BD7151066D7BFF25CC48A5235E IDESourceControlProjectWCConfigurations - - IDESourceControlRepositoryExtensionIdentifierKey - public.vcs.git - IDESourceControlWCCIdentifierKey - 30465C3FE587DE41DC8D7C50D84E40C5B622D54F - IDESourceControlWCCName - - IDESourceControlRepositoryExtensionIdentifierKey public.vcs.git diff --git a/SpringApp.xcodeproj/project.xcworkspace/xcuserdata/mengto.xcuserdatad/UserInterfaceState.xcuserstate b/SpringApp.xcodeproj/project.xcworkspace/xcuserdata/mengto.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index 7b64b70..0000000 Binary files a/SpringApp.xcodeproj/project.xcworkspace/xcuserdata/mengto.xcuserdatad/UserInterfaceState.xcuserstate and /dev/null differ diff --git a/SpringApp.xcodeproj/xcshareddata/xcschemes/Spring.xcscheme b/SpringApp.xcodeproj/xcshareddata/xcschemes/Spring.xcscheme new file mode 100644 index 0000000..ecfaae8 --- /dev/null +++ b/SpringApp.xcodeproj/xcshareddata/xcschemes/Spring.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SpringApp.xcodeproj/xcuserdata/mengto.xcuserdatad/xcschemes/SpringApp.xcscheme b/SpringApp.xcodeproj/xcshareddata/xcschemes/SpringApp.xcscheme similarity index 95% rename from SpringApp.xcodeproj/xcuserdata/mengto.xcuserdatad/xcschemes/SpringApp.xcscheme rename to SpringApp.xcodeproj/xcshareddata/xcschemes/SpringApp.xcscheme index 9ea424c..7d33ad6 100644 --- a/SpringApp.xcodeproj/xcuserdata/mengto.xcuserdatad/xcschemes/SpringApp.xcscheme +++ b/SpringApp.xcodeproj/xcshareddata/xcschemes/SpringApp.xcscheme @@ -1,7 +1,7 @@ + LastUpgradeVersion = "0900" + version = "1.3"> @@ -51,10 +51,10 @@ + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -86,15 +86,18 @@ ReferencedContainer = "container:SpringApp.xcodeproj"> + + @@ -110,10 +113,10 @@ diff --git a/SpringApp.xcodeproj/xcuserdata/mengto.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/SpringApp.xcodeproj/xcuserdata/mengto.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist deleted file mode 100644 index fe2b454..0000000 --- a/SpringApp.xcodeproj/xcuserdata/mengto.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/SpringApp.xcodeproj/xcuserdata/mengto.xcuserdatad/xcschemes/xcschememanagement.plist b/SpringApp.xcodeproj/xcuserdata/mengto.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 48ba9d1..0000000 --- a/SpringApp.xcodeproj/xcuserdata/mengto.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,37 +0,0 @@ - - - - - SchemeUserState - - SpringApp.xcscheme - - orderHint - 0 - - - SuppressBuildableAutocreation - - 1A4FDA321A6E44780099D309 - - primary - - - 1A4FDA3C1A6E44780099D309 - - primary - - - 9641173A1A5BE90A000E3A5A - - primary - - - 9641174F1A5BE90A000E3A5A - - primary - - - - - diff --git a/SpringApp/AppDelegate.swift b/SpringApp/AppDelegate.swift index 68f9cca..fd03978 100644 --- a/SpringApp/AppDelegate.swift +++ b/SpringApp/AppDelegate.swift @@ -14,30 +14,29 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + func applicationDidFinishLaunching(_ application: UIApplication) { // Override point for customization after application launch. - return true } - func applicationWillResignActive(application: UIApplication) { + 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) { + 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) { + 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) { + 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) { + func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } diff --git a/SpringApp/Base.lproj/LaunchScreen.xib b/SpringApp/Base.lproj/LaunchScreen.xib index 3ace0e1..a13447c 100644 --- a/SpringApp/Base.lproj/LaunchScreen.xib +++ b/SpringApp/Base.lproj/LaunchScreen.xib @@ -1,7 +1,9 @@ - + - + + + @@ -11,15 +13,13 @@ - - + - - + diff --git a/SpringApp/Base.lproj/Main.storyboard b/SpringApp/Base.lproj/Main.storyboard index a0437d8..6528bfc 100644 --- a/SpringApp/Base.lproj/Main.storyboard +++ b/SpringApp/Base.lproj/Main.storyboard @@ -1,8 +1,16 @@ - - + + - + + + + + + AvenirNext-DemiBold + AvenirNext-Regular + + @@ -13,15 +21,13 @@ - + - - - + @@ -35,32 +41,29 @@ - + @@ -130,67 +132,58 @@ - - - - + - - - + + - - - + + - - + - - + + - + @@ -210,7 +203,7 @@ - + @@ -256,40 +249,35 @@ - + - - - - - + + + - - + - + @@ -309,7 +297,7 @@ - + @@ -339,146 +327,130 @@ - + - - - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - + @@ -532,7 +504,7 @@ - + diff --git a/SpringApp/CodeViewController.swift b/SpringApp/CodeViewController.swift index 96e4895..9aa8327 100644 --- a/SpringApp/CodeViewController.swift +++ b/SpringApp/CodeViewController.swift @@ -20,7 +20,7 @@ class CodeViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - modalView.transform = CGAffineTransformMakeTranslation(-300, 0) + modalView.transform = CGAffineTransform(translationX: -300, y: 0) if data.animation != "" { codeText += "layer.animation = \"\(data.animation)\"\n" @@ -57,21 +57,23 @@ class CodeViewController: UIViewController { codeTextView.text = codeText } - @IBAction func closeButtonPressed(sender: AnyObject) { - UIApplication.sharedApplication().sendAction("maximizeView:", to: nil, from: self, forEvent: nil) + @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({ - self.dismissViewControllerAnimated(false, completion: nil) + modalView.animateToNext(completion: { + self.dismiss(animated: false, completion: nil) }) } - override func viewDidAppear(animated: Bool) { + override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(true) modalView.animate() - UIApplication.sharedApplication().sendAction("minimizeView:", to: nil, from: self, forEvent: nil) + UIApplication.shared.sendAction(#selector(SpringViewController.minimizeView(_:)), to: nil, from: self, for: nil) } } + + diff --git a/SpringApp/Images.xcassets/AppIcon.appiconset/Contents.json b/SpringApp/Images.xcassets/AppIcon.appiconset/Contents.json index 17d5afa..1acc2b0 100644 --- a/SpringApp/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/SpringApp/Images.xcassets/AppIcon.appiconset/Contents.json @@ -71,6 +71,12 @@ "idiom" : "ipad", "filename" : "appicon@152.png", "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "appicon@167.png", + "scale" : "2x" } ], "info" : { diff --git a/SpringApp/Images.xcassets/AppIcon.appiconset/appicon@167.png b/SpringApp/Images.xcassets/AppIcon.appiconset/appicon@167.png new file mode 100644 index 0000000..9ae2444 Binary files /dev/null and b/SpringApp/Images.xcassets/AppIcon.appiconset/appicon@167.png differ diff --git a/SpringApp/Info.plist b/SpringApp/Info.plist index f5b3473..01aa7f4 100644 --- a/SpringApp/Info.plist +++ b/SpringApp/Info.plist @@ -2,14 +2,12 @@ - UIViewControllerBasedStatusBarAppearance - CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - designcode.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -38,5 +36,7 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIViewControllerBasedStatusBarAppearance + diff --git a/SpringApp/OptionsViewController.swift b/SpringApp/OptionsViewController.swift index 1cba77c..be62011 100644 --- a/SpringApp/OptionsViewController.swift +++ b/SpringApp/OptionsViewController.swift @@ -10,13 +10,13 @@ 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) + 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 { @@ -50,7 +50,7 @@ class OptionsViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - modalView.transform = CGAffineTransformMakeTranslation(0, 300) + modalView.transform = CGAffineTransform(translationX: 0, y: 300) dampingSlider.setValue(Float(data.damping), animated: true) velocitySlider.setValue(Float(data.velocity), animated: true) @@ -67,64 +67,64 @@ class OptionsViewController: UIViewController { rotateLabel.text = getString("Rotate", value: data.rotate) } - @IBAction func dampingSliderChanged(sender: AnyObject) { - selectedDamping = sender.valueForKey("value") as CGFloat + @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.valueForKey("value") as CGFloat + @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.valueForKey("value") as CGFloat + @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.valueForKey("value") as CGFloat + @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.valueForKey("value") as CGFloat + @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.valueForKey("value") as CGFloat + @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) { + @IBAction func resetButtonPressed(_ sender: AnyObject) { delegate?.resetButtonPressed(sender) - dismissViewControllerAnimated(true, completion: nil) + dismiss(animated: true, completion: nil) - UIApplication.sharedApplication().sendAction("maximizeView:", to: nil, from: self, forEvent: nil) + UIApplication.shared.sendAction(#selector(SpringViewController.maximizeView(_:)), to: nil, from: self, for: nil) } - @IBAction func closeButtonPressed(sender: AnyObject) { - dismissViewControllerAnimated(true, completion: nil) + @IBAction func closeButtonPressed(_ sender: AnyObject) { + dismiss(animated: true, completion: nil) - UIApplication.sharedApplication().sendAction("maximizeView:", to: nil, from: self, forEvent: nil) + UIApplication.shared.sendAction(#selector(SpringViewController.maximizeView(_:)), to: nil, from: self, for: nil) } - override func viewDidAppear(animated: Bool) { + override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(true) - UIApplication.sharedApplication().sendAction("minimizeView:", to: nil, from: self, forEvent: nil) + UIApplication.shared.sendAction(#selector(SpringViewController.minimizeView(_:)), to: nil, from: self, for: nil) modalView.animate() } - func getString(name: String, value: CGFloat) -> String { + func getString(_ name: String, value: CGFloat) -> String { return String(format: "\(name): %.1f", Double(value)) } } diff --git a/SpringApp/SpringViewController.swift b/SpringApp/SpringViewController.swift index 42362b7..6a4ea1b 100644 --- a/SpringApp/SpringViewController.swift +++ b/SpringApp/SpringViewController.swift @@ -34,49 +34,49 @@ class SpringViewController: UIViewController, UIPickerViewDelegate, UIPickerView var selectedY: CGFloat = 0 var selectedRotate: CGFloat = 0 - @IBAction func forceSliderChanged(sender: AnyObject) { - selectedForce = sender.valueForKey("value") as CGFloat + @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.valueForKey("value") as CGFloat + @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.valueForKey("value") as CGFloat + @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.valueForKey("value") as CGFloat + func dampingSliderChanged(_ sender: AnyObject) { + selectedDamping = sender.value(forKey: "value") as! CGFloat animateView() } - func velocitySliderChanged(sender: AnyObject) { - selectedVelocity = sender.valueForKey("value") as CGFloat + func velocitySliderChanged(_ sender: AnyObject) { + selectedVelocity = sender.value(forKey: "value") as! CGFloat animateView() } - func scaleSliderChanged(sender: AnyObject) { - selectedScale = sender.valueForKey("value") as CGFloat + func scaleSliderChanged(_ sender: AnyObject) { + selectedScale = sender.value(forKey: "value") as! CGFloat animateView() } - func xSliderChanged(sender: AnyObject) { - selectedX = sender.valueForKey("value") as CGFloat + func xSliderChanged(_ sender: AnyObject) { + selectedX = sender.value(forKey: "value") as! CGFloat animateView() } - func ySliderChanged(sender: AnyObject) { - selectedY = sender.valueForKey("value") as CGFloat + func ySliderChanged(_ sender: AnyObject) { + selectedY = sender.value(forKey: "value") as! CGFloat animateView() } - func rotateSliderChanged(sender: AnyObject) { - selectedRotate = sender.valueForKey("value") as CGFloat + func rotateSliderChanged(_ sender: AnyObject) { + selectedRotate = sender.value(forKey: "value") as! CGFloat animateView() } @@ -98,52 +98,84 @@ class SpringViewController: UIViewController, UIPickerViewDelegate, UIPickerView ballView.y = selectedY ballView.rotate = selectedRotate - ballView.animation = data[0][selectedRow] - ballView.curve = data[1][selectedEasing] + ballView.animation = animations[selectedRow].rawValue + ballView.curve = animationCurves[selectedEasing].rawValue } - func minimizeView(sender: AnyObject) { - spring(0.7, { - self.view.transform = CGAffineTransformMakeScale(0.935, 0.935) + @objc func minimizeView(_ sender: AnyObject) { + SpringAnimation.spring(duration: 0.7, animations: { + self.view.transform = CGAffineTransform(scaleX: 0.935, y: 0.935) }) - UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.LightContent, animated: true) + UIApplication.shared.setStatusBarStyle(UIStatusBarStyle.lightContent, animated: true) } - func maximizeView(sender: AnyObject) { - spring(0.7, { - self.view.transform = CGAffineTransformMakeScale(1, 1) + @objc func maximizeView(_ sender: AnyObject) { + SpringAnimation.spring(duration: 0.7, animations: { + self.view.transform = CGAffineTransform(scaleX: 1, y: 1) }) - UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.Default, animated: true) + UIApplication.shared.setStatusBarStyle(UIStatusBarStyle.default, animated: true) } - - var data = [[ - "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", - ], ["spring", "linear", "easeIn", "easeOut", "easeInOut"]] + + 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() @@ -153,12 +185,12 @@ class SpringViewController: UIViewController, UIPickerViewDelegate, UIPickerView animationPicker.showsSelectionIndicator = true } - @IBAction func ballButtonPressed(sender: AnyObject) { + @IBAction func ballButtonPressed(_ sender: AnyObject) { - UIView.animateWithDuration(0.1, animations: { - self.ballView.backgroundColor = UIColor(hex: "#69DBFF") + UIView.animate(withDuration: 0.1, animations: { + self.ballView.backgroundColor = UIColor(hex: "69DBFF") }, completion: { finished in - UIView.animateWithDuration(0.5, animations: { + UIView.animate(withDuration: 0.5, animations: { self.ballView.backgroundColor = UIColor(hex: "#279CEB") }) }) @@ -166,45 +198,25 @@ class SpringViewController: UIViewController, UIPickerViewDelegate, UIPickerView animateView() } - var ballState = 0 + var isBall = false func changeBall() { - switch ballState { - case 1: - let animation = CABasicAnimation() - animation.keyPath = "cornerRadius" - animation.fromValue = 50 - animation.toValue = 10 - animation.duration = 0.2 - ballView.layer.cornerRadius = 10 - ballView.layer.addAnimation(animation, forKey: "radius") - - ballState = 0 - default: - let animation = CABasicAnimation() - animation.keyPath = "cornerRadius" - animation.fromValue = 10 - animation.toValue = 50 - animation.duration = 0.2 - ballView.layer.cornerRadius = 50 - ballView.layer.addAnimation(animation, forKey: "radius") - - ballState++ - } - + isBall = !isBall let animation = CABasicAnimation() - animation.keyPath = "opacity" - animation.fromValue = 1 - animation.toValue = 0 + 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 - animation.autoreverses = true - ballView.layer.addAnimation(animation, forKey: "fade") + ballView.layer.cornerRadius = cornerRadius + ballView.layer.add(animation, forKey: "radius") } - @IBAction func shapeButtonPressed(sender: AnyObject) { + @IBAction func shapeButtonPressed(_ sender: AnyObject) { changeBall() } - func resetButtonPressed(sender: AnyObject) { + func resetButtonPressed(_ sender: AnyObject) { selectedForce = 1 selectedDuration = 1 selectedDelay = 0 @@ -225,19 +237,19 @@ class SpringViewController: UIViewController, UIPickerViewDelegate, UIPickerView delayLabel.text = String(format: "Delay: %.1f", Double(selectedDelay)) } - func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int { + func numberOfComponents(in pickerView: UIPickerView) -> Int { return 2 } - func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { - return data[component].count + 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 data[component][row] + 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) { + func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { switch component { case 0: selectedRow = row @@ -248,15 +260,15 @@ class SpringViewController: UIViewController, UIPickerViewDelegate, UIPickerView } } - override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { - if let optionsViewController = segue.destinationViewController as? OptionsViewController { + 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.destinationViewController as? CodeViewController { + else if let codeViewController = segue.destination as? CodeViewController { setOptions() codeViewController.data = ballView } } -} \ No newline at end of file +} diff --git a/SpringAppTests/Info.plist b/SpringAppTests/Info.plist index b1fb88b..ba72822 100644 --- a/SpringAppTests/Info.plist +++ b/SpringAppTests/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - designcode.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/SpringAppTests/SpringAppTests.swift b/SpringAppTests/SpringAppTests.swift index 2f0113b..ac2484b 100644 --- a/SpringAppTests/SpringAppTests.swift +++ b/SpringAppTests/SpringAppTests.swift @@ -28,7 +28,7 @@ class SpringAppTests: XCTestCase { func testPerformanceExample() { // This is an example of a performance test case. - self.measureBlock() { + self.measure() { // Put the code you want to measure the time of here. } } diff --git a/SpringTests/Info.plist b/SpringTests/Info.plist index 609467f..ba72822 100644 --- a/SpringTests/Info.plist +++ b/SpringTests/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - com.jamztang.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/SpringTests/SpringTests.swift b/SpringTests/SpringTests.swift index 105bf23..938737d 100644 --- a/SpringTests/SpringTests.swift +++ b/SpringTests/SpringTests.swift @@ -28,7 +28,7 @@ class SpringTests: XCTestCase { func testPerformanceExample() { // This is an example of a performance test case. - self.measureBlock() { + self.measure() { // Put the code you want to measure the time of here. } }