From d5cec871b3ee517edf6a6dfe7e212684835379f4 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 6 Dec 2018 16:05:31 +0700 Subject: [PATCH 01/13] Updates-to-review --- .../CustomTextField/CustomTextField.swift | 170 ++++++++++++++++++ .../CustomTextField/CustomTextField.xib | 84 +++++++++ .../Classes/Regex.swift | 58 ++++++ .../Classes/SelectableLinkController.swift | 109 +++++++++++ .../StanwoodTransitions/CustomAnimator.swift | 146 +++++++++++++++ .../CustomNavigationController.swift | 72 ++++++++ .../StanwoodViewController.swift | 41 +++++ .../UIView+AnimatedTransition.swift | 138 ++++++++++++++ .../ViewAnimationBundle.swift | 28 +++ StanwoodCore/Extensions/Array+Extension.swift | 81 +++++++++ .../CABasicAnimation+Extension.swift | 24 +++ .../CGAffineTransform+Extension.swift | 35 ++++ .../Extensions/CGRect+Extension.swift | 29 +++ StanwoodCore/Extensions/Date+Extension.swift | 52 ++++++ .../Extensions/Dictionary+Extension.swift | 20 +++ StanwoodCore/Extensions/Error+Extension.swift | 32 ++++ .../Extensions/String+Extension.swift | 48 +++++ .../UIActivityIndicatorView+Extension.swift | 27 +++ .../Extensions/UIApplication+Extension.swift | 5 + StanwoodCore/Extensions/UIView+Dismiss.swift | 41 +++++ StanwoodCore/Extensions/UIView+Effects.swift | 77 ++++++++ StanwoodCore/Extensions/UIView+Loading.swift | 39 ++++ .../UIViewController+Utilities.swift | 104 +++++++++++ .../Extensions/UIWindow+Extension.swift | 18 ++ StanwoodCore/Extensions/URL+Extension.swift | 43 +++++ 25 files changed, 1521 insertions(+) create mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/Classes/CustomTextField/CustomTextField.swift create mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/Classes/CustomTextField/CustomTextField.xib create mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/Classes/Regex.swift create mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/Classes/SelectableLinkController.swift create mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/CustomAnimator.swift create mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/CustomNavigationController.swift create mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/StanwoodViewController.swift create mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/UIView+AnimatedTransition.swift create mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/ViewAnimationBundle.swift create mode 100644 StanwoodCore/Extensions/Array+Extension.swift create mode 100644 StanwoodCore/Extensions/CABasicAnimation+Extension.swift create mode 100644 StanwoodCore/Extensions/CGAffineTransform+Extension.swift create mode 100644 StanwoodCore/Extensions/CGRect+Extension.swift create mode 100644 StanwoodCore/Extensions/Date+Extension.swift create mode 100644 StanwoodCore/Extensions/Dictionary+Extension.swift create mode 100644 StanwoodCore/Extensions/Error+Extension.swift create mode 100644 StanwoodCore/Extensions/UIActivityIndicatorView+Extension.swift create mode 100644 StanwoodCore/Extensions/UIView+Dismiss.swift create mode 100644 StanwoodCore/Extensions/UIView+Effects.swift create mode 100644 StanwoodCore/Extensions/UIView+Loading.swift create mode 100644 StanwoodCore/Extensions/UIViewController+Utilities.swift create mode 100644 StanwoodCore/Extensions/UIWindow+Extension.swift create mode 100644 StanwoodCore/Extensions/URL+Extension.swift diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/Classes/CustomTextField/CustomTextField.swift b/StanwoodCore/DELETE-Classes to review and find homes for/Classes/CustomTextField/CustomTextField.swift new file mode 100644 index 0000000..c6603d3 --- /dev/null +++ b/StanwoodCore/DELETE-Classes to review and find homes for/Classes/CustomTextField/CustomTextField.swift @@ -0,0 +1,170 @@ +// +// CustomTextField.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 27/11/2018. +// Copyright © 2018 Stanwood GmbH. All rights reserved. +// + +import UIKit + + +extension UIColor{ + static var errorRed = UIColor(r: 208, g: 2, b: 27) + static var labelGrey = UIColor(r: 155, g: 155, b: 155) +} + +enum CustomTextFieldType{ + case email, name +} + +protocol CustomTextFieldDelegate: class { + func textFieldShouldReturn(_ textField: UITextField) -> Bool + func textFieldDidChange(_ textField: UITextField) +} + +class CustomTextField: UIView { + + + // MARK:- Public Properties + + + var text: String?{ + get{ + return textfield.text + } + } + + weak var delegate: CustomTextFieldDelegate? + + + // MARK:- Private Properties + + + private var isValid = true { + didSet{ + let image = (textfield.text?.isEmpty ?? true) ? nil : validIcon + validImageView.image = image + titleLabel.textColor = isValid ? .labelGrey : .errorRed + errorLabel.isHidden = isValid + } + } + private var hasText = false { + didSet{ + UIView.animate(withDuration: .medium) { + self.titleCentredConstraint.priority = UILayoutPriority(rawValue: self.hasText ? 900 : 900) + self.layoutIfNeeded() + } + } + } + private var validIcon: UIImage { + get{ + return isValid ? #imageLiteral(resourceName: "icCheckCircle") : #imageLiteral(resourceName: "icError") + } + } + private var hideErrorLabels = false + private var type: CustomTextFieldType = .email + + + // MARK:- Outlets + + + @IBOutlet private weak var validImageView: UIImageView! + @IBOutlet private weak var titleLabel: UILabel! + @IBOutlet private weak var errorLabel: UILabel! + @IBOutlet weak var textfield: UITextField! + @IBOutlet private weak var titleCentredConstraint: NSLayoutConstraint! + + + // MARK:- Init + + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + + loadFromOutlet() + layoutViews() + } + + + // MARK:- Private Functions + + + private func layoutViews(){ + errorLabel.textColor = .errorRed + isValid = false + errorLabel.isHidden = hideErrorLabels + hasText = false + textfield.delegate = self + backgroundColor = .clear + isValid = true + } + + private func validate(_ type: CustomTextFieldType){ + switch type { + case .email: + isValid = textfield.text?.isValid(.email) ?? false + case .name: + isValid = textfield.text?.isValid(.name) ?? false + } + } + + + // MARK:- Public Functions + + + func configure(for type: CustomTextFieldType){ + + self.type = type + + switch type { + case .email: + titleLabel.text = "Email" + errorLabel.text = "Invalid Email" + textfield.keyboardType = .emailAddress + textfield.autocapitalizationType = .none + textfield.placeholder = "jon@stanwood.io" + case .name: + titleLabel.text = "Name" + errorLabel.text = "Invalid Name" + textfield.keyboardType = .default + textfield.autocapitalizationType = .words + textfield.placeholder = "Jon S Wood" + } + } + + + // MARK:- Actions + + + @IBAction func textFieldChanged(_ sender: UITextField) { + + guard sender.text?.count ?? 0 > 0 else { + isValid = true + return + } + + hasText = !(sender.text?.isEmpty ?? true) + validate(type) + delegate?.textFieldDidChange(sender) + } +} + + +// MARK:- UITextFieldDelegate + + +extension CustomTextField: UITextFieldDelegate { + + func textFieldDidBeginEditing(_ textField: UITextField) { + hasText = true + } + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + return delegate?.textFieldShouldReturn(textField) ?? true + } + + func textFieldDidEndEditing(_ textField: UITextField) { + hasText = textField.text?.trimmedLowerCase.count ?? 0 > 0 + } +} diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/Classes/CustomTextField/CustomTextField.xib b/StanwoodCore/DELETE-Classes to review and find homes for/Classes/CustomTextField/CustomTextField.xib new file mode 100644 index 0000000..ffdc08d --- /dev/null +++ b/StanwoodCore/DELETE-Classes to review and find homes for/Classes/CustomTextField/CustomTextField.xib @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/Classes/Regex.swift b/StanwoodCore/DELETE-Classes to review and find homes for/Classes/Regex.swift new file mode 100644 index 0000000..b036d16 --- /dev/null +++ b/StanwoodCore/DELETE-Classes to review and find homes for/Classes/Regex.swift @@ -0,0 +1,58 @@ +// +// Regex.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 27/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import Foundation + + +struct Regex { + static let email = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}" + static let name = "^(?=.{1,40}$)[a-zA-Z]+(?:[a-zA-Z]+)*$" +} + + +extension NSRegularExpression { + + func isValid(for string: String) -> Bool { + return firstMatch(in: string, options: [], range: string.range) != nil + } +} + +enum RegexType{ + case email, name // TODO:- Password +} + +extension String { + + func isValid(_ type: RegexType) -> Bool{ + + switch type { + case .email: + return Regex.email.regularExpression?.isValid(for: self) ?? false + case .name: + return !(Regex.name.regularExpression?.isValid(for: self) ?? true) && isTwoOrMoreWords + } + } + + var isTwoOrMoreWords: Bool { + + let words = components(separatedBy:" ") + + guard + words.count > 1, + words[1].count > 0 + else{ + return false + } + + return true + } + + var regularExpression: NSRegularExpression? { + return try? NSRegularExpression(pattern: self, options: .caseInsensitive) + } +} diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/Classes/SelectableLinkController.swift b/StanwoodCore/DELETE-Classes to review and find homes for/Classes/SelectableLinkController.swift new file mode 100644 index 0000000..9a78004 --- /dev/null +++ b/StanwoodCore/DELETE-Classes to review and find homes for/Classes/SelectableLinkController.swift @@ -0,0 +1,109 @@ +// +// SelectableLinkController.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 27/11/2018. +// Copyright © 2018 Stanwood GmbH. All rights reserved. +// + +import Foundation + +/* + + Subclassing this controller allows us to insert links into a `UITextView` + + + `LinkObject` consists of 2 properties + - `var link = ""` + - `var text = ""` + + + By passing and array of `LinkObject` to `setUpTouchesFor(_ textView: UITextView, with mainText: String, with objects:[LinkObject]?, with color: UIColor = .white)` We inspect all object and take the range of `text` and turn it into a link. The link is then handled in the delegate extension below + + This currently only supports `UIViewController` + + */ + + + + +class SelectableLinkController: UIViewController, UITextViewDelegate { + + @available(iOS 10.0, *) + func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { + UIApplication.shared.open(URL, options: [:]) + return false + } + + func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool { + UIApplication.shared.open(URL, options: [:]) + return false + } + + func setUpTouchesFor(_ textView: UITextView, with mainText: String, with objects:[LinkObject]?, with color: UIColor = .gray){ + + guard let objects = objects else { return } + + let attributedString = NSMutableAttributedString(string: mainText) + + objects.forEach { (object) in + + if let range = mainText.range(of: object.text) { + let nsRange = mainText.nsRange(from: range) + attributedString.addAttribute(.link, value: object.link, range: nsRange) + attributedString.addAttribute(NSAttributedString.Key.underlineStyle , value: NSUnderlineStyle.single.rawValue, range: nsRange) + } + } + + attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: color, range: NSRange(location: 0, length: attributedString.length - 1)) + + textView.tintColor = color + textView.attributedText = attributedString + textView.delegate = self + } +} + + + +enum LinkType{ + case conditions + case privacy +} + +struct LinkObject { + + + // MARK:- Properties + + + var link = "" + var text = "" + + + // MARK:- Functions + + + static var termsAndConditions: [LinkObject] { + + let terms = LinkObject.type(with: .conditions) + let privacy = LinkObject.type(with: .privacy) + return [terms, privacy] + } + + + + static func type(with type: LinkType) -> LinkObject{ + + switch type { + case .conditions: + return LinkObject(link: "Add URL" , text: "Add Text") + case .privacy: + return LinkObject(link: "Add URL", text: "Add Text") + } + } +} + + +extension LinkObject { + +} diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/CustomAnimator.swift b/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/CustomAnimator.swift new file mode 100644 index 0000000..fcb79dd --- /dev/null +++ b/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/CustomAnimator.swift @@ -0,0 +1,146 @@ +// +// CustomAnimator.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 16/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import UIKit + +class CustomAnimator: NSObject, UIViewControllerAnimatedTransitioning { + + // MARK:- Properties + + + var duration : TimeInterval + var isPresenting : Bool + var sourceView : UIView? + var transitionType: TransitionType + var backgoundColour: UIColor + var shouldClipToBounds : Bool + + + // MARK:- Init + + + init(duration: TimeInterval, isPresenting: Bool, sourceView: UIView?, transitionType: TransitionType = .opaque, backgoundColour: UIColor = .white, shouldClipToBounds: Bool) { + self.duration = duration + self.isPresenting = isPresenting + self.sourceView = sourceView + self.transitionType = transitionType + self.backgoundColour = backgoundColour + self.shouldClipToBounds = shouldClipToBounds + } + + + // MARK:- UIViewControllerAnimatedTransitioning + + + func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { + return duration + } + + func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { + + let container = transitionContext.containerView + + /// Main Frames + guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from) else { return } + guard let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) else { return } + + toView.backgroundColor = backgoundColour + fromView.backgroundColor = backgoundColour + container.backgroundColor = backgoundColour + + /// Add container to hold our animating views + self.isPresenting ? container.addSubview(toView) : container.insertSubview(toView, belowSubview: fromView) + + /// Get instance of where we are animating to so we can grab any view instances + let detailView = isPresenting ? toView : fromView + + /// Set size and Alpha + toView.frame = isPresenting ? CGRect(x: fromView.frame.width, y: 0, width: toView.frame.width, height: toView.frame.height) : toView.frame + + toView.layoutIfNeeded() + + /// Loop through all views to grab the views with custom tags + guard let animatingViews = sourceView?.subViewsWithTransitionID else { + transitionContext.completeTransition(!transitionContext.transitionWasCancelled) + return + } + + /// Create bundles for each view we want to animate + let bundles = UIView.createAnimationBundleFrom(animatingViews, with: detailView, and: isPresenting, from: fromView, transitionType: transitionType, shouldClipToBounds: shouldClipToBounds) + + /// Add the view we will animate, but hide the one we animate from and to + bundles.forEach { (bundle) in + + container.addSubview(bundle.animatingView) + bundle.destinationView.alpha = 0 + bundle.orignalView.alpha = 0 + + if bundle.transitionType == .fade { + bundle.destinationViewCopy.alpha = self.isPresenting ? 0 : 1 + container.addSubview(bundle.destinationViewCopy) + detailView.frame = fromView.frame + toView.alpha = 0 + } + } + + fromView.layoutIfNeeded() + toView.layoutIfNeeded() + container.layoutIfNeeded() + + UIView.animate(withDuration: duration, animations: { + + /// Animate to the new frames + bundles.forEach { (bundle) in + + bundle.animatingView.frame = self.isPresenting ? bundle.newFrame : bundle.originalFrame + bundle.animatingView.layer.cornerRadius = self.isPresenting ? bundle.destinationView.layer.cornerRadius : bundle.orignalView.layer.cornerRadius + + if bundle.transitionType == .fade { + bundle.animatingView.alpha = 0 + bundle.destinationViewCopy.frame = bundle.animatingView.frame + bundle.destinationViewCopy.alpha = self.isPresenting ? 1 : 0 + bundle.destinationViewCopy.layer.cornerRadius = bundle.animatingView.layer.cornerRadius + + toView.alpha = 1 + } + + + if !self.isPresenting, bundle.transitionType == .fade { + bundle.animatingView.alpha = 1 + } + + fromView.alpha = 0 + } + + detailView.frame = self.isPresenting ? fromView.frame : toView.frame + + fromView.layoutIfNeeded() + toView.layoutIfNeeded() + container.layoutIfNeeded() + detailView.layoutIfNeeded() + + }, completion: { (finished) in + + /// When we have finished, we remove the views we animated and add then orginals back + bundles.forEach { (bundle) in + bundle.animatingView.alpha = 1 + bundle.orignalView.alpha = 1 + bundle.animatingView.removeFromSuperview() + bundle.destinationViewCopy.removeFromSuperview() + bundle.destinationView.alpha = 1 + } + + detailView.alpha = 1 + toView.alpha = 1 + + transitionContext.completeTransition(!transitionContext.transitionWasCancelled) + }) + } + +} + diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/CustomNavigationController.swift b/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/CustomNavigationController.swift new file mode 100644 index 0000000..576cb08 --- /dev/null +++ b/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/CustomNavigationController.swift @@ -0,0 +1,72 @@ +// +// CustomNavigationController.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 16/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import UIKit + +class CustomNavigationController: UINavigationController, UIViewControllerTransitioningDelegate { + + + // MARK:- Properties + + + var sourceView: UIView! + var transitionType: TransitionType = .opaque + var backgoundColour: UIColor = .white + var transitionDuration: Double = 0.8 + var shouldClipToBounds = false + + + // MARK:- LifeCycle + + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = backgoundColour + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + setUpControllersForCustomTransitions() + delegate = self + } + + + // MARK:- Private Functions + + + private func setUpControllersForCustomTransitions(){ + + /// For this case we set all child controllers to the custom transition + for controller in viewControllers{ + controller.modalPresentationStyle = .custom + controller.transitioningDelegate = self + } + } + +} + +// MARK:- UINavigationControllerDelegate + + +extension CustomNavigationController: UINavigationControllerDelegate{ + + func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { + + let isPresting = operation == .push + + return CustomAnimator(duration: transitionDuration, isPresenting: isPresting, sourceView: sourceView, transitionType: transitionType, backgoundColour: backgoundColour, shouldClipToBounds: shouldClipToBounds) + } +} + + + + + + + diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/StanwoodViewController.swift b/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/StanwoodViewController.swift new file mode 100644 index 0000000..ba480f7 --- /dev/null +++ b/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/StanwoodViewController.swift @@ -0,0 +1,41 @@ +// +// StanwoodViewController.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 17/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import UIKit + +class StanwoodViewController: UIViewController { + + + // MARK:- Properties + + + var transitionType: TransitionType = .opaque + var transitionDuration: Double = 0.8 + var backgroundColour: UIColor = .white + var shouldClipToBounds = false +} + + + + +// MARK:- UIViewControllerTransitioningDelegate + + +extension StanwoodViewController: UIViewControllerTransitioningDelegate { + + func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { + + return CustomAnimator(duration: transitionDuration, isPresenting: true, sourceView: view, transitionType: transitionType, backgoundColour: backgroundColour, shouldClipToBounds: shouldClipToBounds) + } + + func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { + return CustomAnimator(duration: transitionDuration, isPresenting: false, sourceView: view, transitionType: transitionType, backgoundColour: backgroundColour, shouldClipToBounds: shouldClipToBounds) + } +} + + diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/UIView+AnimatedTransition.swift b/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/UIView+AnimatedTransition.swift new file mode 100644 index 0000000..0e35f47 --- /dev/null +++ b/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/UIView+AnimatedTransition.swift @@ -0,0 +1,138 @@ +// +// UIView+AnimatedTransition.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 16/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import Foundation +import UIKit + + +extension UIView { + + /// Use this to set the id of views. + @IBInspectable + var transitionID: String { + get { + return self.restorationIdentifier ?? "" + } + set { + /// Not sure hacking this is the wisest move but we need a string property on all views without making a sublcass. And this is better than a tag for now :D + self.restorationIdentifier = newValue + } + } + + /// Grab all subviews with a custom tag + func viewWithID(_ id: String) -> UIView? { + let selectedView = subViewsWithTransitionID.filter { $0.transitionID == id}.first + return selectedView + } + + /// Property to grab all views with transitionIDS from source view + var subViewsWithTransitionID: [UIView] { + return UIView.getAllSubviewsWithTransitionIDSFrom(self) + } + + /// Grab all subviews with a custom ID + static func getAllSubviewsWithTransitionIDSFrom(_ view: UIView) -> [UIView] { + return view.subviews.flatMap { subView -> [UIView] in + var result = getAllSubviewsWithTransitionIDSFrom(subView) as [UIView] + let view = subView as UIView + if !view.transitionID.isEmpty { + result.append(view) + } + return result + } + } + + /// Create all bundles + static func createAnimationBundleFrom(_ views: [UIView], with detailView: UIView, and isPresenting: Bool, from fromView: UIView, transitionType: TransitionType, shouldClipToBounds: Bool) -> [ViewAnimationBundle] { + + var viewAnimationBundles = [ViewAnimationBundle]() + + views.forEach { (view) in + + if let animationBundle = createViewBundle(with: detailView, from: view, and: isPresenting, from: fromView, transitionType: transitionType, shouldClipToBounds: shouldClipToBounds){ + viewAnimationBundles.append(animationBundle) + } + } + + return viewAnimationBundles + } + + static func createViewBundle(with destinationView: UIView, from orignalView: UIView, and isPresenting: Bool, from fromView: UIView, transitionType: TransitionType, shouldClipToBounds: Bool) -> ViewAnimationBundle? { + + guard + let destination = destinationView.viewWithID(orignalView.transitionID), + let mainWindow = UIApplication.shared.keyWindow + else { + return nil + + } + + destination.clipsToBounds = true + + /// Create to and from Views + let newImageViewFrame = mainWindow.convert(destination.frame, from: fromView) + let originalImageViewFrame = mainWindow.convert(orignalView.frame, from: orignalView.superview) + + /// Create temporary view to animate + let animatingImageView = UIImageView(frame: isPresenting ? originalImageViewFrame : newImageViewFrame) + + + /// Image view get a real image in an image view so it animates changes better tan a snapshot + /// TODO:- Do the same to support blurs + if let imageView = orignalView as? UIImageView { + + animatingImageView.image = imageView.image + animatingImageView.contentMode = .scaleAspectFill + animatingImageView.clipsToBounds = true + let detailImageView = destination as? UIImageView + detailImageView?.image = imageView.image + + }else{ + + animatingImageView.image = orignalView.asImage + animatingImageView.contentMode = .scaleAspectFill + animatingImageView.clipsToBounds = shouldClipToBounds + } + + let destinationCopyView = UIImageView(frame: isPresenting ? originalImageViewFrame : newImageViewFrame) + destinationCopyView.image = destination.asImage + destinationCopyView.contentMode = .scaleAspectFill + destinationCopyView.clipsToBounds = shouldClipToBounds + + /// Set radius if we need to chnange it + animatingImageView.layer.cornerRadius = isPresenting ? orignalView.layer.cornerRadius : destination.layer.cornerRadius + animatingImageView.layoutIfNeeded() + + return ViewAnimationBundle(animatingView: animatingImageView, orignalView: orignalView, destinationView: destination ,destinationViewCopy: destinationCopyView, newFrame: newImageViewFrame, originalFrame: originalImageViewFrame, transitionType: transitionType, index: 0) + } + + var asImage: UIImage? { + + if #available(iOS 10.0, *) { + let renderer = UIGraphicsImageRenderer(bounds: bounds) + return renderer.image { rendererContext in + layer.render(in: rendererContext.cgContext) + } + } else { + + UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0.0) + defer { UIGraphicsEndImageContext() } + guard let currentContext = UIGraphicsGetCurrentContext() else { + return nil + } + self.layer.render(in: currentContext) + return UIGraphicsGetImageFromCurrentImageContext() + } + } + + +} + + + + diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/ViewAnimationBundle.swift b/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/ViewAnimationBundle.swift new file mode 100644 index 0000000..64d4be0 --- /dev/null +++ b/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/ViewAnimationBundle.swift @@ -0,0 +1,28 @@ +// +// ViewAnimationBundle.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 16/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import Foundation +import UIKit + +enum TransitionType{ + case fade + case opaque +} + +// Contains all objects needed to animate one view to the next + +struct ViewAnimationBundle { + var animatingView: UIView! + var orignalView: UIView! + var destinationView: UIView! + var destinationViewCopy: UIView! + var newFrame: CGRect! + var originalFrame: CGRect! + var transitionType: TransitionType = .opaque + var index = 0 +} diff --git a/StanwoodCore/Extensions/Array+Extension.swift b/StanwoodCore/Extensions/Array+Extension.swift new file mode 100644 index 0000000..4d4d4b8 --- /dev/null +++ b/StanwoodCore/Extensions/Array+Extension.swift @@ -0,0 +1,81 @@ +// +// Extension+Array.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 27/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import Foundation + + +extension Array { + + var uniqueItems: Array { + return NSSet(array: self).allObjects as? Array ?? self + } +} + +extension Array where Element: UIView { + + func hideWithCascade(withDelay delay:TimeInterval = .instant, withAnimationtime animationTime:TimeInterval = .instant, withOffset offset: CGFloat = 10){ + + for item in self{ + + let delay = (TimeInterval((self.index(of: item)) ?? 0) * delay) + delay + + let indexAsFloat = CGFloat(self.index(of: item) ?? 0) + let newOffset = (indexAsFloat * offset) + offset + + UIView.animate(withDuration: animationTime, delay: delay, usingSpringWithDamping: .highDamping, initialSpringVelocity: .mediumSpring, options: [.allowAnimatedContent, .allowUserInteraction], animations: { + + item.transform = CGAffineTransform.init(scaleX: 0.8, y: 0.8).translatedBy(x: 0, y: newOffset) + item.alpha = .clear + + item.layoutIfNeeded() + + }, completion: nil) + } + } + + func showWithCascade(withDelay delay:TimeInterval = .halfTiny, withAnimationtime animationTime:TimeInterval = .medium, withOffset offset: CGFloat = 10){ + + for item in self{ + + let indexAsDouble = Double(index(of: item) ?? 0) + + let delay = (indexAsDouble * delay) + delay + + UIView.animate(withDuration: animationTime, delay: delay, usingSpringWithDamping: .highDamping, initialSpringVelocity: .mediumSpring, options: [.allowAnimatedContent, .allowUserInteraction], animations: { + + item.transform = CGAffineTransform.identity + item.alpha = .full + + item.layoutIfNeeded() + + }, completion: nil) + } + } + + func setViewsHidden(_ hidden: Bool){ + + let alpha: CGFloat = hidden ? .clear : .full + let transfom: CGAffineTransform = hidden ? .shrink : .identity + let delay: TimeInterval = hidden ? .instant : .medium + UIView.animate(withDuration: .normal, delay: delay, options: .allowAnimatedContent, animations: { + + self.forEach { (item) in + item.alpha = alpha + item.transform = transfom + item.layoutIfNeeded() + } + + }, completion: { (done) in + + self.forEach { (item) in + item.transform = .identity + item.layoutIfNeeded() + } + }) + } +} diff --git a/StanwoodCore/Extensions/CABasicAnimation+Extension.swift b/StanwoodCore/Extensions/CABasicAnimation+Extension.swift new file mode 100644 index 0000000..d12f2db --- /dev/null +++ b/StanwoodCore/Extensions/CABasicAnimation+Extension.swift @@ -0,0 +1,24 @@ +// +// CABasicAnimation+Extension.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 27/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import Foundation + +extension CABasicAnimation { + + public static var rotating: CABasicAnimation { + + let duration: Double = 10 + let animtion = CABasicAnimation(keyPath: "transform.rotation.z") + animtion.toValue = .pi * 2.0 * duration + animtion.duration = duration + animtion.isCumulative = true + animtion.repeatCount = 500000 + + return animtion + } +} diff --git a/StanwoodCore/Extensions/CGAffineTransform+Extension.swift b/StanwoodCore/Extensions/CGAffineTransform+Extension.swift new file mode 100644 index 0000000..9b206c2 --- /dev/null +++ b/StanwoodCore/Extensions/CGAffineTransform+Extension.swift @@ -0,0 +1,35 @@ +// +// CGAffineTransform+Extension.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 27/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import Foundation + +extension CGAffineTransform { + + + // MARK:- Properties + + + /// CGAffineTransform(scaleX: 0.8, y: 0.8) + public static var shrink: CGAffineTransform { return CGAffineTransform(scaleX: 0.8, y: 0.8) } + + /// CGAffineTransform(scaleX: 0.3, y: 0.3) + public static var small: CGAffineTransform { return CGAffineTransform(scaleX: 0.3, y: 0.3) } + + /// CGAffineTransform(scaleX: 0.001, y: 0.001) + public static var tiny: CGAffineTransform { return CGAffineTransform(scaleX: 0.001, y: 0.001) } + + + // MARK:- Functions + + + /// Combine two transforms + static func translate(transform: CGAffineTransform, withX x: CGFloat = 0, withY y: CGFloat = 0) -> CGAffineTransform{ + + return transform.translatedBy(x: x, y: y) + } +} diff --git a/StanwoodCore/Extensions/CGRect+Extension.swift b/StanwoodCore/Extensions/CGRect+Extension.swift new file mode 100644 index 0000000..dce41bd --- /dev/null +++ b/StanwoodCore/Extensions/CGRect+Extension.swift @@ -0,0 +1,29 @@ +// +// CGRect+Extension.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 27/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import Foundation + + +extension CGRect { + + var width: CGFloat{ + return size.width + } + + var height: CGFloat{ + return size.height + } + + var halfWidth: CGFloat{ + return width / 2 + } + + var halfHeight: CGFloat{ + return height / 2 + } +} diff --git a/StanwoodCore/Extensions/Date+Extension.swift b/StanwoodCore/Extensions/Date+Extension.swift new file mode 100644 index 0000000..edff76c --- /dev/null +++ b/StanwoodCore/Extensions/Date+Extension.swift @@ -0,0 +1,52 @@ +// +// Date+Extension.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 27/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import Foundation + +extension Date { + + static func dateFormatter(with format: String = "yyyy-MM-dd'T'HH:mm:ssZZZZZ") -> DateFormatter { + + let formatter = DateFormatter() + formatter.locale = Locale.current + formatter.dateFormat = format + + return formatter + } + public func dateString(from format: String? = "dd.MM.yyyy") -> String{ + + let formatter = DateFormatter() + formatter.dateFormat = format + + return formatter.string(from: self) + } + + var isInThePast: Bool{ + return self.timeIntervalSinceNow.sign == .minus + } + + var isToday: Bool{ + return Calendar.current.isDate( Date(), inSameDayAs: self) + } + + var yesterday: Date { + return Calendar.current.date(byAdding: .day, value: -1, to: noon)! + } + var tomorrow: Date { + return Calendar.current.date(byAdding: .day, value: 1, to: noon)! + } + var noon: Date { + return Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)! + } + var month: Int { + return Calendar.current.component(.month, from: self) + } + var isLastDayOfMonth: Bool { + return tomorrow.month != month + } +} diff --git a/StanwoodCore/Extensions/Dictionary+Extension.swift b/StanwoodCore/Extensions/Dictionary+Extension.swift new file mode 100644 index 0000000..74f7312 --- /dev/null +++ b/StanwoodCore/Extensions/Dictionary+Extension.swift @@ -0,0 +1,20 @@ +// +// Dictionary+Extension.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 27/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import Foundation + + +extension Dictionary { + + mutating func add(_ otherDictionary: Dictionary) { + + for (key,value) in otherDictionary { + self.updateValue(value, forKey:key) + } + } +} diff --git a/StanwoodCore/Extensions/Error+Extension.swift b/StanwoodCore/Extensions/Error+Extension.swift new file mode 100644 index 0000000..a40f299 --- /dev/null +++ b/StanwoodCore/Extensions/Error+Extension.swift @@ -0,0 +1,32 @@ +// +// Error+Extension.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 27/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import Foundation + + +extension NSError { + + convenience init(withMessage message:String, withDomain domain: String = "de.stanwood.error", withCode code: Int? = 0, withLog log: String? = "Error") { + + let userInfo: [AnyHashable : Any] = [NSLocalizedDescriptionKey : message , NSLocalizedFailureReasonErrorKey : log ?? message] + self.init(domain: domain, code: code ?? 0, userInfo: (userInfo as! [String : Any])) + } + + + convenience init(with error:Error?, withDomain domain: String = "de.stanwood.error", withCode code: Int? = 0, withLog log: String? = "Error") { + let message = error?.localizedDescription ?? "" + let userInfo: [AnyHashable : Any] = [NSLocalizedDescriptionKey : message , NSLocalizedFailureReasonErrorKey : log ?? message] + self.init(domain: domain, code: code ?? 0, userInfo: (userInfo as! [String : Any])) + } +} + +extension Error { + + var code: Int { return (self as NSError).code } + var domain: String { return (self as NSError).domain } +} diff --git a/StanwoodCore/Extensions/String+Extension.swift b/StanwoodCore/Extensions/String+Extension.swift index 9793e5d..0c1866b 100644 --- a/StanwoodCore/Extensions/String+Extension.swift +++ b/StanwoodCore/Extensions/String+Extension.swift @@ -62,4 +62,52 @@ public extension String { var phoneFormat: String { return "tel://\(self)" } + + var range: NSRange { + return NSRange(location: 0, length: count) + } + + var noWhiteSpace: String { + return trimmingCharacters(in: .whitespacesAndNewlines) + } + + var trimmedLowerCase: String { + return noWhiteSpace.lowercased() + } + + func replace(_ oldText: String, with newText: String) -> String { + return replacingOccurrences(of: oldText, with: newText) + } + + func remove(_ char: String) -> String{ + return replacingOccurrences(of: char, with: "") + } + + func swapAllText(between firstString: String, and secondString: String, with newText: String) -> String{ + + let regex = "(?s)(?<=\(firstString)).*(?=\(secondString))" + let matched = matches(for: regex, in: self) + return replace(matched.first ?? "", with: newText) + } + + func matches(for regex: String, in text: String) -> [String] { + + do { + let regex = try NSRegularExpression(pattern: regex) + let results = regex.matches(in: text, range: NSRange(text.startIndex..., in: text)) + return results.map { + String(text[Range($0.range, in: text)!]) + } + } catch let error { + print("invalid regex: \(error.localizedDescription)") + return [] + } + } +} + + +extension StringProtocol where Index == String.Index { + func nsRange(from range: Range) -> NSRange { + return NSRange(range, in: self) + } } diff --git a/StanwoodCore/Extensions/UIActivityIndicatorView+Extension.swift b/StanwoodCore/Extensions/UIActivityIndicatorView+Extension.swift new file mode 100644 index 0000000..9939d90 --- /dev/null +++ b/StanwoodCore/Extensions/UIActivityIndicatorView+Extension.swift @@ -0,0 +1,27 @@ +// +// UIActivityIndicatorView+Extension.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 27/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import Foundation + + + +extension UIActivityIndicatorView { + + func show(_ show: Bool) { + + main { [weak self] in + + switch show { + case true: + self?.startAnimating() + case false: + self?.stopAnimating() + } + } + } +} diff --git a/StanwoodCore/Extensions/UIApplication+Extension.swift b/StanwoodCore/Extensions/UIApplication+Extension.swift index 747b031..0e1dfff 100644 --- a/StanwoodCore/Extensions/UIApplication+Extension.swift +++ b/StanwoodCore/Extensions/UIApplication+Extension.swift @@ -48,4 +48,9 @@ extension UIApplication { return base } } + + static func open(url: String?){ + guard let url = URL(string: url ?? "") else { return } + UIApplication.shared.open(url, options: [:], completionHandler: nil) + } } diff --git a/StanwoodCore/Extensions/UIView+Dismiss.swift b/StanwoodCore/Extensions/UIView+Dismiss.swift new file mode 100644 index 0000000..37ca151 --- /dev/null +++ b/StanwoodCore/Extensions/UIView+Dismiss.swift @@ -0,0 +1,41 @@ +// +// UIView+Dismiss.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 27/11/2018. +// Copyright © 2018 Stanwood GmbH. All rights reserved. +// + +import Foundation + + +@objc protocol DismissView { + + func dismiss() + @objc optional func dismissViewAnimating() + @objc optional func dismissViewCompleted() +} + +extension DismissView where Self: UIView { + + func dismiss(with duration: TimeInterval, shouldDismissSelf dismiss: Bool = true){ + + UIView.animate(withDuration: duration, animations: { + + self.dismissViewAnimating?() + + self.alpha = .clear + self.transform = .shrink + self.layoutIfNeeded() + + }) { (complete) in + + if dismiss { + + self.removeFromSuperview() + } + + self.dismissViewCompleted?() + } + } +} diff --git a/StanwoodCore/Extensions/UIView+Effects.swift b/StanwoodCore/Extensions/UIView+Effects.swift new file mode 100644 index 0000000..700227c --- /dev/null +++ b/StanwoodCore/Extensions/UIView+Effects.swift @@ -0,0 +1,77 @@ +// +// UIView+Extension.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 27/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import Foundation + +typealias ViewCompletionBlock = () -> Void? + + +extension UIView { + + func removeAllSubViews(){ + + subviews.forEach { (view) in + view.removeFromSuperview() + } + } + + + func addGradientWith(colors: [CGColor]){ + + let gradient = CAGradientLayer() + gradient.frame = bounds + gradient.colors = colors + layer.insertSublayer(gradient, at: 0) + } + + func hideAndShrink(_ animated: Bool = false, with customDuration: TimeInterval = .normal, _ completion: ViewCompletionBlock? = nil){ + + let duration: TimeInterval = animated ? customDuration : .instant + + UIView.animate(withDuration: duration, animations: { + + self.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) + self.alpha = .clear + + }) { (done) in + + completion?() + } + } + + func expandAndShow(_ animated: Bool = true, with customDuration: TimeInterval = .normal, _ completion: ViewCompletionBlock? = nil){ + + let duration: TimeInterval = animated ? customDuration : .instant + + UIView.animate(withDuration: duration, animations: { + + self.transform = CGAffineTransform.identity + self.alpha = .full + + }) { (done) in + + completion?() + } + } + + func rotate(_ rotate: Bool){ + + if rotate { + layer.removeAllAnimations() + layer.add(CABasicAnimation.rotating, forKey: "rotationAnimation") + }else{ + layer.removeAllAnimations() + } + + } + + func removeShadow(){ + layer.shadowRadius = 0 + layer.shadowOpacity = 0 + } +} diff --git a/StanwoodCore/Extensions/UIView+Loading.swift b/StanwoodCore/Extensions/UIView+Loading.swift new file mode 100644 index 0000000..07f93b8 --- /dev/null +++ b/StanwoodCore/Extensions/UIView+Loading.swift @@ -0,0 +1,39 @@ +// +// UIView+Loading.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 27/11/2018. +// Copyright © 2018 Stanwood GmbH. All rights reserved. +// + +import Foundation + +@objc protocol PresentView { + + func present() + @objc optional func presentViewAnimating() + @objc optional func presentViewCompleted() +} + +extension PresentView where Self: UIView { + + func present(with duration: TimeInterval){ + + alpha = .clear + transform = .shrink + layoutIfNeeded() + + UIView.animate(withDuration: duration, animations: { + + self.presentViewAnimating?() + + self.alpha = .full + self.transform = CGAffineTransform.identity + self.layoutIfNeeded() + + }) { (complete) in + + self.presentViewCompleted?() + } + } +} diff --git a/StanwoodCore/Extensions/UIViewController+Utilities.swift b/StanwoodCore/Extensions/UIViewController+Utilities.swift new file mode 100644 index 0000000..c40285a --- /dev/null +++ b/StanwoodCore/Extensions/UIViewController+Utilities.swift @@ -0,0 +1,104 @@ +// +// UIViewController+Extension.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 27/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import Foundation +import SafariServices +import MessageUI +import MapKit + +typealias ViewControllerCompletionBlock = () -> Void? +typealias ViewControllerSuccessBlock = (_ success: Bool) -> Void + + +extension UIViewController: MFMailComposeViewControllerDelegate { + + func showSafariViewController(from urlString: String?){ + + guard + let string = urlString, + let url = URL(string: string) + else{ return } + + let safariController = SFSafariViewController(url: url) + present(safariController, animated: true, completion: nil) + } + + + @discardableResult + func sendEmailIfPossible(to emails: [String]?, + subject: String? = nil, + tintColour: UIColor = .blue, + backUpURL: String? = "www.Stanwood.io", + body: String? = nil, + isHTML: Bool = false, + _ completion: ViewControllerSuccessBlock? = nil) -> Bool{ + + if MFMailComposeViewController.canSendMail() { + + let mailController = MFMailComposeViewController() + mailController.mailComposeDelegate = self + mailController.setToRecipients(emails) + + if let subject = subject { + mailController.setSubject(subject) + } + + if let body = body { + mailController.setMessageBody(body, isHTML: isHTML) + } + + mailController.navigationBar.tintColor = tintColour + mailController.navigationBar.barTintColor = .white + mailController.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor : tintColour] + + completion?(true) + present(mailController, animated: true, completion: nil) + return true + + }else{ + + completion?(false) + guard let url = URL(string: backUpURL ?? "") else { return false } + UIApplication.shared.open(url, options: [:], completionHandler: nil) + return false + } + } + + func pushIfPossible(with controller: UIViewController, _ completion: ViewControllerCompletionBlock? = nil){ + + if let navigationController = navigationController { + navigationController.pushViewController(controller, animated: true) + }else{ + present(controller, animated: true) { + completion?() + } + } + } + + func openMapFor(_ location: CLLocation, withRegion distance: CLLocationDistance? = 10000, withTitle title: String? = "") { + + let coordinates = location.coordinate + let unwrappedDistance = distance ?? 10000 + + let regionSpan = MKCoordinateRegion(center: coordinates, latitudinalMeters: unwrappedDistance, longitudinalMeters: unwrappedDistance) + + let options = [ + MKLaunchOptionsMapCenterKey: NSValue(mkCoordinate: regionSpan.center), + MKLaunchOptionsMapSpanKey: NSValue(mkCoordinateSpan: regionSpan.span) + ] + + let placemark = MKPlacemark(coordinate: coordinates, addressDictionary: nil) + let mapItem = MKMapItem(placemark: placemark) + mapItem.name = title + mapItem.openInMaps(launchOptions: options) + } + + public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { + controller.dismiss(animated: true, completion: nil) + } +} diff --git a/StanwoodCore/Extensions/UIWindow+Extension.swift b/StanwoodCore/Extensions/UIWindow+Extension.swift new file mode 100644 index 0000000..4391044 --- /dev/null +++ b/StanwoodCore/Extensions/UIWindow+Extension.swift @@ -0,0 +1,18 @@ +// +// UIWindow+Extension.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 27/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import Foundation + + +class PassthroughView: UIWindow { + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + let view = super.hitTest(point, with: event) + return view == self ? nil : view + } +} diff --git a/StanwoodCore/Extensions/URL+Extension.swift b/StanwoodCore/Extensions/URL+Extension.swift new file mode 100644 index 0000000..58add4c --- /dev/null +++ b/StanwoodCore/Extensions/URL+Extension.swift @@ -0,0 +1,43 @@ +// +// URL+Extension.swift +// Custom Transitions +// +// Created by Aaron Tredrea on 27/11/2018. +// Copyright © 2018 Stanwood. All rights reserved. +// + +import Foundation + + +extension URL { + + var queryParameters: [String : String]? { + + guard + let components = URLComponents(url: self, resolvingAgainstBaseURL: true), + let queryItems = components.queryItems + else { + return nil + } + + var parameters = [String: String]() + + for item in queryItems { + + parameters[item.name] = item.value + } + + return parameters + } + + func value(for key: String) -> String{ + + guard + let queryParameters = queryParameters, + let item = queryParameters[key] + else{ + return "" + } + return item + } +} From a9f82cac4c4b10f41f4419ec81e999e52e5cb343 Mon Sep 17 00:00:00 2001 From: aaronTredrea Date: Fri, 11 Jan 2019 09:06:14 +0700 Subject: [PATCH 02/13] Delete CustomTextField.swift --- .../CustomTextField/CustomTextField.swift | 170 ------------------ 1 file changed, 170 deletions(-) delete mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/Classes/CustomTextField/CustomTextField.swift diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/Classes/CustomTextField/CustomTextField.swift b/StanwoodCore/DELETE-Classes to review and find homes for/Classes/CustomTextField/CustomTextField.swift deleted file mode 100644 index c6603d3..0000000 --- a/StanwoodCore/DELETE-Classes to review and find homes for/Classes/CustomTextField/CustomTextField.swift +++ /dev/null @@ -1,170 +0,0 @@ -// -// CustomTextField.swift -// Custom Transitions -// -// Created by Aaron Tredrea on 27/11/2018. -// Copyright © 2018 Stanwood GmbH. All rights reserved. -// - -import UIKit - - -extension UIColor{ - static var errorRed = UIColor(r: 208, g: 2, b: 27) - static var labelGrey = UIColor(r: 155, g: 155, b: 155) -} - -enum CustomTextFieldType{ - case email, name -} - -protocol CustomTextFieldDelegate: class { - func textFieldShouldReturn(_ textField: UITextField) -> Bool - func textFieldDidChange(_ textField: UITextField) -} - -class CustomTextField: UIView { - - - // MARK:- Public Properties - - - var text: String?{ - get{ - return textfield.text - } - } - - weak var delegate: CustomTextFieldDelegate? - - - // MARK:- Private Properties - - - private var isValid = true { - didSet{ - let image = (textfield.text?.isEmpty ?? true) ? nil : validIcon - validImageView.image = image - titleLabel.textColor = isValid ? .labelGrey : .errorRed - errorLabel.isHidden = isValid - } - } - private var hasText = false { - didSet{ - UIView.animate(withDuration: .medium) { - self.titleCentredConstraint.priority = UILayoutPriority(rawValue: self.hasText ? 900 : 900) - self.layoutIfNeeded() - } - } - } - private var validIcon: UIImage { - get{ - return isValid ? #imageLiteral(resourceName: "icCheckCircle") : #imageLiteral(resourceName: "icError") - } - } - private var hideErrorLabels = false - private var type: CustomTextFieldType = .email - - - // MARK:- Outlets - - - @IBOutlet private weak var validImageView: UIImageView! - @IBOutlet private weak var titleLabel: UILabel! - @IBOutlet private weak var errorLabel: UILabel! - @IBOutlet weak var textfield: UITextField! - @IBOutlet private weak var titleCentredConstraint: NSLayoutConstraint! - - - // MARK:- Init - - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - - loadFromOutlet() - layoutViews() - } - - - // MARK:- Private Functions - - - private func layoutViews(){ - errorLabel.textColor = .errorRed - isValid = false - errorLabel.isHidden = hideErrorLabels - hasText = false - textfield.delegate = self - backgroundColor = .clear - isValid = true - } - - private func validate(_ type: CustomTextFieldType){ - switch type { - case .email: - isValid = textfield.text?.isValid(.email) ?? false - case .name: - isValid = textfield.text?.isValid(.name) ?? false - } - } - - - // MARK:- Public Functions - - - func configure(for type: CustomTextFieldType){ - - self.type = type - - switch type { - case .email: - titleLabel.text = "Email" - errorLabel.text = "Invalid Email" - textfield.keyboardType = .emailAddress - textfield.autocapitalizationType = .none - textfield.placeholder = "jon@stanwood.io" - case .name: - titleLabel.text = "Name" - errorLabel.text = "Invalid Name" - textfield.keyboardType = .default - textfield.autocapitalizationType = .words - textfield.placeholder = "Jon S Wood" - } - } - - - // MARK:- Actions - - - @IBAction func textFieldChanged(_ sender: UITextField) { - - guard sender.text?.count ?? 0 > 0 else { - isValid = true - return - } - - hasText = !(sender.text?.isEmpty ?? true) - validate(type) - delegate?.textFieldDidChange(sender) - } -} - - -// MARK:- UITextFieldDelegate - - -extension CustomTextField: UITextFieldDelegate { - - func textFieldDidBeginEditing(_ textField: UITextField) { - hasText = true - } - - func textFieldShouldReturn(_ textField: UITextField) -> Bool { - return delegate?.textFieldShouldReturn(textField) ?? true - } - - func textFieldDidEndEditing(_ textField: UITextField) { - hasText = textField.text?.trimmedLowerCase.count ?? 0 > 0 - } -} From a482bd06391f94aaa98b9e3fcf0a3f08e7012770 Mon Sep 17 00:00:00 2001 From: aaronTredrea Date: Fri, 11 Jan 2019 09:06:22 +0700 Subject: [PATCH 03/13] Delete CustomTextField.xib --- .../CustomTextField/CustomTextField.xib | 84 ------------------- 1 file changed, 84 deletions(-) delete mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/Classes/CustomTextField/CustomTextField.xib diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/Classes/CustomTextField/CustomTextField.xib b/StanwoodCore/DELETE-Classes to review and find homes for/Classes/CustomTextField/CustomTextField.xib deleted file mode 100644 index ffdc08d..0000000 --- a/StanwoodCore/DELETE-Classes to review and find homes for/Classes/CustomTextField/CustomTextField.xib +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 87fba4708e138c9791619e8f7c6af39feec37962 Mon Sep 17 00:00:00 2001 From: aaronTredrea Date: Fri, 11 Jan 2019 09:06:34 +0700 Subject: [PATCH 04/13] Delete Regex.swift --- .../Classes/Regex.swift | 58 ------------------- 1 file changed, 58 deletions(-) delete mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/Classes/Regex.swift diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/Classes/Regex.swift b/StanwoodCore/DELETE-Classes to review and find homes for/Classes/Regex.swift deleted file mode 100644 index b036d16..0000000 --- a/StanwoodCore/DELETE-Classes to review and find homes for/Classes/Regex.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// Regex.swift -// Custom Transitions -// -// Created by Aaron Tredrea on 27/11/2018. -// Copyright © 2018 Stanwood. All rights reserved. -// - -import Foundation - - -struct Regex { - static let email = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}" - static let name = "^(?=.{1,40}$)[a-zA-Z]+(?:[a-zA-Z]+)*$" -} - - -extension NSRegularExpression { - - func isValid(for string: String) -> Bool { - return firstMatch(in: string, options: [], range: string.range) != nil - } -} - -enum RegexType{ - case email, name // TODO:- Password -} - -extension String { - - func isValid(_ type: RegexType) -> Bool{ - - switch type { - case .email: - return Regex.email.regularExpression?.isValid(for: self) ?? false - case .name: - return !(Regex.name.regularExpression?.isValid(for: self) ?? true) && isTwoOrMoreWords - } - } - - var isTwoOrMoreWords: Bool { - - let words = components(separatedBy:" ") - - guard - words.count > 1, - words[1].count > 0 - else{ - return false - } - - return true - } - - var regularExpression: NSRegularExpression? { - return try? NSRegularExpression(pattern: self, options: .caseInsensitive) - } -} From 46a789ce1c0853c266e554d63aa31de3d8d2c00a Mon Sep 17 00:00:00 2001 From: aaronTredrea Date: Fri, 11 Jan 2019 09:06:52 +0700 Subject: [PATCH 05/13] Delete SelectableLinkController.swift --- .../Classes/SelectableLinkController.swift | 109 ------------------ 1 file changed, 109 deletions(-) delete mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/Classes/SelectableLinkController.swift diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/Classes/SelectableLinkController.swift b/StanwoodCore/DELETE-Classes to review and find homes for/Classes/SelectableLinkController.swift deleted file mode 100644 index 9a78004..0000000 --- a/StanwoodCore/DELETE-Classes to review and find homes for/Classes/SelectableLinkController.swift +++ /dev/null @@ -1,109 +0,0 @@ -// -// SelectableLinkController.swift -// Custom Transitions -// -// Created by Aaron Tredrea on 27/11/2018. -// Copyright © 2018 Stanwood GmbH. All rights reserved. -// - -import Foundation - -/* - - Subclassing this controller allows us to insert links into a `UITextView` - - - `LinkObject` consists of 2 properties - - `var link = ""` - - `var text = ""` - - - By passing and array of `LinkObject` to `setUpTouchesFor(_ textView: UITextView, with mainText: String, with objects:[LinkObject]?, with color: UIColor = .white)` We inspect all object and take the range of `text` and turn it into a link. The link is then handled in the delegate extension below - - This currently only supports `UIViewController` - - */ - - - - -class SelectableLinkController: UIViewController, UITextViewDelegate { - - @available(iOS 10.0, *) - func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { - UIApplication.shared.open(URL, options: [:]) - return false - } - - func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool { - UIApplication.shared.open(URL, options: [:]) - return false - } - - func setUpTouchesFor(_ textView: UITextView, with mainText: String, with objects:[LinkObject]?, with color: UIColor = .gray){ - - guard let objects = objects else { return } - - let attributedString = NSMutableAttributedString(string: mainText) - - objects.forEach { (object) in - - if let range = mainText.range(of: object.text) { - let nsRange = mainText.nsRange(from: range) - attributedString.addAttribute(.link, value: object.link, range: nsRange) - attributedString.addAttribute(NSAttributedString.Key.underlineStyle , value: NSUnderlineStyle.single.rawValue, range: nsRange) - } - } - - attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: color, range: NSRange(location: 0, length: attributedString.length - 1)) - - textView.tintColor = color - textView.attributedText = attributedString - textView.delegate = self - } -} - - - -enum LinkType{ - case conditions - case privacy -} - -struct LinkObject { - - - // MARK:- Properties - - - var link = "" - var text = "" - - - // MARK:- Functions - - - static var termsAndConditions: [LinkObject] { - - let terms = LinkObject.type(with: .conditions) - let privacy = LinkObject.type(with: .privacy) - return [terms, privacy] - } - - - - static func type(with type: LinkType) -> LinkObject{ - - switch type { - case .conditions: - return LinkObject(link: "Add URL" , text: "Add Text") - case .privacy: - return LinkObject(link: "Add URL", text: "Add Text") - } - } -} - - -extension LinkObject { - -} From a03b0a8f69b248a34b408ca2597f1703ea580eea Mon Sep 17 00:00:00 2001 From: aaronTredrea Date: Fri, 11 Jan 2019 09:07:00 +0700 Subject: [PATCH 06/13] Delete CustomAnimator.swift --- .../StanwoodTransitions/CustomAnimator.swift | 146 ------------------ 1 file changed, 146 deletions(-) delete mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/CustomAnimator.swift diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/CustomAnimator.swift b/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/CustomAnimator.swift deleted file mode 100644 index fcb79dd..0000000 --- a/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/CustomAnimator.swift +++ /dev/null @@ -1,146 +0,0 @@ -// -// CustomAnimator.swift -// Custom Transitions -// -// Created by Aaron Tredrea on 16/11/2018. -// Copyright © 2018 Stanwood. All rights reserved. -// - -import UIKit - -class CustomAnimator: NSObject, UIViewControllerAnimatedTransitioning { - - // MARK:- Properties - - - var duration : TimeInterval - var isPresenting : Bool - var sourceView : UIView? - var transitionType: TransitionType - var backgoundColour: UIColor - var shouldClipToBounds : Bool - - - // MARK:- Init - - - init(duration: TimeInterval, isPresenting: Bool, sourceView: UIView?, transitionType: TransitionType = .opaque, backgoundColour: UIColor = .white, shouldClipToBounds: Bool) { - self.duration = duration - self.isPresenting = isPresenting - self.sourceView = sourceView - self.transitionType = transitionType - self.backgoundColour = backgoundColour - self.shouldClipToBounds = shouldClipToBounds - } - - - // MARK:- UIViewControllerAnimatedTransitioning - - - func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { - return duration - } - - func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { - - let container = transitionContext.containerView - - /// Main Frames - guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from) else { return } - guard let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) else { return } - - toView.backgroundColor = backgoundColour - fromView.backgroundColor = backgoundColour - container.backgroundColor = backgoundColour - - /// Add container to hold our animating views - self.isPresenting ? container.addSubview(toView) : container.insertSubview(toView, belowSubview: fromView) - - /// Get instance of where we are animating to so we can grab any view instances - let detailView = isPresenting ? toView : fromView - - /// Set size and Alpha - toView.frame = isPresenting ? CGRect(x: fromView.frame.width, y: 0, width: toView.frame.width, height: toView.frame.height) : toView.frame - - toView.layoutIfNeeded() - - /// Loop through all views to grab the views with custom tags - guard let animatingViews = sourceView?.subViewsWithTransitionID else { - transitionContext.completeTransition(!transitionContext.transitionWasCancelled) - return - } - - /// Create bundles for each view we want to animate - let bundles = UIView.createAnimationBundleFrom(animatingViews, with: detailView, and: isPresenting, from: fromView, transitionType: transitionType, shouldClipToBounds: shouldClipToBounds) - - /// Add the view we will animate, but hide the one we animate from and to - bundles.forEach { (bundle) in - - container.addSubview(bundle.animatingView) - bundle.destinationView.alpha = 0 - bundle.orignalView.alpha = 0 - - if bundle.transitionType == .fade { - bundle.destinationViewCopy.alpha = self.isPresenting ? 0 : 1 - container.addSubview(bundle.destinationViewCopy) - detailView.frame = fromView.frame - toView.alpha = 0 - } - } - - fromView.layoutIfNeeded() - toView.layoutIfNeeded() - container.layoutIfNeeded() - - UIView.animate(withDuration: duration, animations: { - - /// Animate to the new frames - bundles.forEach { (bundle) in - - bundle.animatingView.frame = self.isPresenting ? bundle.newFrame : bundle.originalFrame - bundle.animatingView.layer.cornerRadius = self.isPresenting ? bundle.destinationView.layer.cornerRadius : bundle.orignalView.layer.cornerRadius - - if bundle.transitionType == .fade { - bundle.animatingView.alpha = 0 - bundle.destinationViewCopy.frame = bundle.animatingView.frame - bundle.destinationViewCopy.alpha = self.isPresenting ? 1 : 0 - bundle.destinationViewCopy.layer.cornerRadius = bundle.animatingView.layer.cornerRadius - - toView.alpha = 1 - } - - - if !self.isPresenting, bundle.transitionType == .fade { - bundle.animatingView.alpha = 1 - } - - fromView.alpha = 0 - } - - detailView.frame = self.isPresenting ? fromView.frame : toView.frame - - fromView.layoutIfNeeded() - toView.layoutIfNeeded() - container.layoutIfNeeded() - detailView.layoutIfNeeded() - - }, completion: { (finished) in - - /// When we have finished, we remove the views we animated and add then orginals back - bundles.forEach { (bundle) in - bundle.animatingView.alpha = 1 - bundle.orignalView.alpha = 1 - bundle.animatingView.removeFromSuperview() - bundle.destinationViewCopy.removeFromSuperview() - bundle.destinationView.alpha = 1 - } - - detailView.alpha = 1 - toView.alpha = 1 - - transitionContext.completeTransition(!transitionContext.transitionWasCancelled) - }) - } - -} - From ff61223679766a9e9dfb83ca9b75c163fc2e0e11 Mon Sep 17 00:00:00 2001 From: aaronTredrea Date: Fri, 11 Jan 2019 09:07:08 +0700 Subject: [PATCH 07/13] Delete CustomNavigationController.swift --- .../CustomNavigationController.swift | 72 ------------------- 1 file changed, 72 deletions(-) delete mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/CustomNavigationController.swift diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/CustomNavigationController.swift b/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/CustomNavigationController.swift deleted file mode 100644 index 576cb08..0000000 --- a/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/CustomNavigationController.swift +++ /dev/null @@ -1,72 +0,0 @@ -// -// CustomNavigationController.swift -// Custom Transitions -// -// Created by Aaron Tredrea on 16/11/2018. -// Copyright © 2018 Stanwood. All rights reserved. -// - -import UIKit - -class CustomNavigationController: UINavigationController, UIViewControllerTransitioningDelegate { - - - // MARK:- Properties - - - var sourceView: UIView! - var transitionType: TransitionType = .opaque - var backgoundColour: UIColor = .white - var transitionDuration: Double = 0.8 - var shouldClipToBounds = false - - - // MARK:- LifeCycle - - - override func viewDidLoad() { - super.viewDidLoad() - view.backgroundColor = backgoundColour - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - setUpControllersForCustomTransitions() - delegate = self - } - - - // MARK:- Private Functions - - - private func setUpControllersForCustomTransitions(){ - - /// For this case we set all child controllers to the custom transition - for controller in viewControllers{ - controller.modalPresentationStyle = .custom - controller.transitioningDelegate = self - } - } - -} - -// MARK:- UINavigationControllerDelegate - - -extension CustomNavigationController: UINavigationControllerDelegate{ - - func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { - - let isPresting = operation == .push - - return CustomAnimator(duration: transitionDuration, isPresenting: isPresting, sourceView: sourceView, transitionType: transitionType, backgoundColour: backgoundColour, shouldClipToBounds: shouldClipToBounds) - } -} - - - - - - - From 2495e00464644b4daeddf12b8e3990a205e622c4 Mon Sep 17 00:00:00 2001 From: aaronTredrea Date: Fri, 11 Jan 2019 09:07:16 +0700 Subject: [PATCH 08/13] Delete StanwoodViewController.swift --- .../StanwoodViewController.swift | 41 ------------------- 1 file changed, 41 deletions(-) delete mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/StanwoodViewController.swift diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/StanwoodViewController.swift b/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/StanwoodViewController.swift deleted file mode 100644 index ba480f7..0000000 --- a/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/StanwoodViewController.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// StanwoodViewController.swift -// Custom Transitions -// -// Created by Aaron Tredrea on 17/11/2018. -// Copyright © 2018 Stanwood. All rights reserved. -// - -import UIKit - -class StanwoodViewController: UIViewController { - - - // MARK:- Properties - - - var transitionType: TransitionType = .opaque - var transitionDuration: Double = 0.8 - var backgroundColour: UIColor = .white - var shouldClipToBounds = false -} - - - - -// MARK:- UIViewControllerTransitioningDelegate - - -extension StanwoodViewController: UIViewControllerTransitioningDelegate { - - func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { - - return CustomAnimator(duration: transitionDuration, isPresenting: true, sourceView: view, transitionType: transitionType, backgoundColour: backgroundColour, shouldClipToBounds: shouldClipToBounds) - } - - func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { - return CustomAnimator(duration: transitionDuration, isPresenting: false, sourceView: view, transitionType: transitionType, backgoundColour: backgroundColour, shouldClipToBounds: shouldClipToBounds) - } -} - - From 94bf88fd8745d0898f98947e9477a4e17d99424e Mon Sep 17 00:00:00 2001 From: aaronTredrea Date: Fri, 11 Jan 2019 09:07:27 +0700 Subject: [PATCH 09/13] Delete UIView+AnimatedTransition.swift --- .../UIView+AnimatedTransition.swift | 138 ------------------ 1 file changed, 138 deletions(-) delete mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/UIView+AnimatedTransition.swift diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/UIView+AnimatedTransition.swift b/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/UIView+AnimatedTransition.swift deleted file mode 100644 index 0e35f47..0000000 --- a/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/UIView+AnimatedTransition.swift +++ /dev/null @@ -1,138 +0,0 @@ -// -// UIView+AnimatedTransition.swift -// Custom Transitions -// -// Created by Aaron Tredrea on 16/11/2018. -// Copyright © 2018 Stanwood. All rights reserved. -// - -import Foundation -import UIKit - - -extension UIView { - - /// Use this to set the id of views. - @IBInspectable - var transitionID: String { - get { - return self.restorationIdentifier ?? "" - } - set { - /// Not sure hacking this is the wisest move but we need a string property on all views without making a sublcass. And this is better than a tag for now :D - self.restorationIdentifier = newValue - } - } - - /// Grab all subviews with a custom tag - func viewWithID(_ id: String) -> UIView? { - let selectedView = subViewsWithTransitionID.filter { $0.transitionID == id}.first - return selectedView - } - - /// Property to grab all views with transitionIDS from source view - var subViewsWithTransitionID: [UIView] { - return UIView.getAllSubviewsWithTransitionIDSFrom(self) - } - - /// Grab all subviews with a custom ID - static func getAllSubviewsWithTransitionIDSFrom(_ view: UIView) -> [UIView] { - return view.subviews.flatMap { subView -> [UIView] in - var result = getAllSubviewsWithTransitionIDSFrom(subView) as [UIView] - let view = subView as UIView - if !view.transitionID.isEmpty { - result.append(view) - } - return result - } - } - - /// Create all bundles - static func createAnimationBundleFrom(_ views: [UIView], with detailView: UIView, and isPresenting: Bool, from fromView: UIView, transitionType: TransitionType, shouldClipToBounds: Bool) -> [ViewAnimationBundle] { - - var viewAnimationBundles = [ViewAnimationBundle]() - - views.forEach { (view) in - - if let animationBundle = createViewBundle(with: detailView, from: view, and: isPresenting, from: fromView, transitionType: transitionType, shouldClipToBounds: shouldClipToBounds){ - viewAnimationBundles.append(animationBundle) - } - } - - return viewAnimationBundles - } - - static func createViewBundle(with destinationView: UIView, from orignalView: UIView, and isPresenting: Bool, from fromView: UIView, transitionType: TransitionType, shouldClipToBounds: Bool) -> ViewAnimationBundle? { - - guard - let destination = destinationView.viewWithID(orignalView.transitionID), - let mainWindow = UIApplication.shared.keyWindow - else { - return nil - - } - - destination.clipsToBounds = true - - /// Create to and from Views - let newImageViewFrame = mainWindow.convert(destination.frame, from: fromView) - let originalImageViewFrame = mainWindow.convert(orignalView.frame, from: orignalView.superview) - - /// Create temporary view to animate - let animatingImageView = UIImageView(frame: isPresenting ? originalImageViewFrame : newImageViewFrame) - - - /// Image view get a real image in an image view so it animates changes better tan a snapshot - /// TODO:- Do the same to support blurs - if let imageView = orignalView as? UIImageView { - - animatingImageView.image = imageView.image - animatingImageView.contentMode = .scaleAspectFill - animatingImageView.clipsToBounds = true - let detailImageView = destination as? UIImageView - detailImageView?.image = imageView.image - - }else{ - - animatingImageView.image = orignalView.asImage - animatingImageView.contentMode = .scaleAspectFill - animatingImageView.clipsToBounds = shouldClipToBounds - } - - let destinationCopyView = UIImageView(frame: isPresenting ? originalImageViewFrame : newImageViewFrame) - destinationCopyView.image = destination.asImage - destinationCopyView.contentMode = .scaleAspectFill - destinationCopyView.clipsToBounds = shouldClipToBounds - - /// Set radius if we need to chnange it - animatingImageView.layer.cornerRadius = isPresenting ? orignalView.layer.cornerRadius : destination.layer.cornerRadius - animatingImageView.layoutIfNeeded() - - return ViewAnimationBundle(animatingView: animatingImageView, orignalView: orignalView, destinationView: destination ,destinationViewCopy: destinationCopyView, newFrame: newImageViewFrame, originalFrame: originalImageViewFrame, transitionType: transitionType, index: 0) - } - - var asImage: UIImage? { - - if #available(iOS 10.0, *) { - let renderer = UIGraphicsImageRenderer(bounds: bounds) - return renderer.image { rendererContext in - layer.render(in: rendererContext.cgContext) - } - } else { - - UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0.0) - defer { UIGraphicsEndImageContext() } - guard let currentContext = UIGraphicsGetCurrentContext() else { - return nil - } - self.layer.render(in: currentContext) - return UIGraphicsGetImageFromCurrentImageContext() - } - } - - -} - - - - From 8a89756a2fa5d983e074e8e349add5d22a0e6c65 Mon Sep 17 00:00:00 2001 From: aaronTredrea Date: Fri, 11 Jan 2019 09:07:42 +0700 Subject: [PATCH 10/13] Delete ViewAnimationBundle.swift --- .../ViewAnimationBundle.swift | 28 ------------------- 1 file changed, 28 deletions(-) delete mode 100644 StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/ViewAnimationBundle.swift diff --git a/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/ViewAnimationBundle.swift b/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/ViewAnimationBundle.swift deleted file mode 100644 index 64d4be0..0000000 --- a/StanwoodCore/DELETE-Classes to review and find homes for/StanwoodTransitions/ViewAnimationBundle.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// ViewAnimationBundle.swift -// Custom Transitions -// -// Created by Aaron Tredrea on 16/11/2018. -// Copyright © 2018 Stanwood. All rights reserved. -// - -import Foundation -import UIKit - -enum TransitionType{ - case fade - case opaque -} - -// Contains all objects needed to animate one view to the next - -struct ViewAnimationBundle { - var animatingView: UIView! - var orignalView: UIView! - var destinationView: UIView! - var destinationViewCopy: UIView! - var newFrame: CGRect! - var originalFrame: CGRect! - var transitionType: TransitionType = .opaque - var index = 0 -} From 0fd88ddb20b487ba74f8d4d9da883ca8de8140ca Mon Sep 17 00:00:00 2001 From: aaronTredrea Date: Fri, 11 Jan 2019 09:07:51 +0700 Subject: [PATCH 11/13] Delete Array+Extension.swift --- StanwoodCore/Extensions/Array+Extension.swift | 81 ------------------- 1 file changed, 81 deletions(-) delete mode 100644 StanwoodCore/Extensions/Array+Extension.swift diff --git a/StanwoodCore/Extensions/Array+Extension.swift b/StanwoodCore/Extensions/Array+Extension.swift deleted file mode 100644 index 4d4d4b8..0000000 --- a/StanwoodCore/Extensions/Array+Extension.swift +++ /dev/null @@ -1,81 +0,0 @@ -// -// Extension+Array.swift -// Custom Transitions -// -// Created by Aaron Tredrea on 27/11/2018. -// Copyright © 2018 Stanwood. All rights reserved. -// - -import Foundation - - -extension Array { - - var uniqueItems: Array { - return NSSet(array: self).allObjects as? Array ?? self - } -} - -extension Array where Element: UIView { - - func hideWithCascade(withDelay delay:TimeInterval = .instant, withAnimationtime animationTime:TimeInterval = .instant, withOffset offset: CGFloat = 10){ - - for item in self{ - - let delay = (TimeInterval((self.index(of: item)) ?? 0) * delay) + delay - - let indexAsFloat = CGFloat(self.index(of: item) ?? 0) - let newOffset = (indexAsFloat * offset) + offset - - UIView.animate(withDuration: animationTime, delay: delay, usingSpringWithDamping: .highDamping, initialSpringVelocity: .mediumSpring, options: [.allowAnimatedContent, .allowUserInteraction], animations: { - - item.transform = CGAffineTransform.init(scaleX: 0.8, y: 0.8).translatedBy(x: 0, y: newOffset) - item.alpha = .clear - - item.layoutIfNeeded() - - }, completion: nil) - } - } - - func showWithCascade(withDelay delay:TimeInterval = .halfTiny, withAnimationtime animationTime:TimeInterval = .medium, withOffset offset: CGFloat = 10){ - - for item in self{ - - let indexAsDouble = Double(index(of: item) ?? 0) - - let delay = (indexAsDouble * delay) + delay - - UIView.animate(withDuration: animationTime, delay: delay, usingSpringWithDamping: .highDamping, initialSpringVelocity: .mediumSpring, options: [.allowAnimatedContent, .allowUserInteraction], animations: { - - item.transform = CGAffineTransform.identity - item.alpha = .full - - item.layoutIfNeeded() - - }, completion: nil) - } - } - - func setViewsHidden(_ hidden: Bool){ - - let alpha: CGFloat = hidden ? .clear : .full - let transfom: CGAffineTransform = hidden ? .shrink : .identity - let delay: TimeInterval = hidden ? .instant : .medium - UIView.animate(withDuration: .normal, delay: delay, options: .allowAnimatedContent, animations: { - - self.forEach { (item) in - item.alpha = alpha - item.transform = transfom - item.layoutIfNeeded() - } - - }, completion: { (done) in - - self.forEach { (item) in - item.transform = .identity - item.layoutIfNeeded() - } - }) - } -} From 8f775431f8e2b445c6928ab84ead6e8fb1df0ec6 Mon Sep 17 00:00:00 2001 From: aaronTredrea Date: Fri, 11 Jan 2019 09:08:03 +0700 Subject: [PATCH 12/13] Delete CABasicAnimation+Extension.swift --- .../CABasicAnimation+Extension.swift | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 StanwoodCore/Extensions/CABasicAnimation+Extension.swift diff --git a/StanwoodCore/Extensions/CABasicAnimation+Extension.swift b/StanwoodCore/Extensions/CABasicAnimation+Extension.swift deleted file mode 100644 index d12f2db..0000000 --- a/StanwoodCore/Extensions/CABasicAnimation+Extension.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// CABasicAnimation+Extension.swift -// Custom Transitions -// -// Created by Aaron Tredrea on 27/11/2018. -// Copyright © 2018 Stanwood. All rights reserved. -// - -import Foundation - -extension CABasicAnimation { - - public static var rotating: CABasicAnimation { - - let duration: Double = 10 - let animtion = CABasicAnimation(keyPath: "transform.rotation.z") - animtion.toValue = .pi * 2.0 * duration - animtion.duration = duration - animtion.isCumulative = true - animtion.repeatCount = 500000 - - return animtion - } -} From 0a507f778537f1d33567459bb23d3eadcf0d74c5 Mon Sep 17 00:00:00 2001 From: aaronTredrea Date: Fri, 11 Jan 2019 09:08:35 +0700 Subject: [PATCH 13/13] Delete UIView+Dismiss.swift --- StanwoodCore/Extensions/UIView+Dismiss.swift | 41 -------------------- 1 file changed, 41 deletions(-) delete mode 100644 StanwoodCore/Extensions/UIView+Dismiss.swift diff --git a/StanwoodCore/Extensions/UIView+Dismiss.swift b/StanwoodCore/Extensions/UIView+Dismiss.swift deleted file mode 100644 index 37ca151..0000000 --- a/StanwoodCore/Extensions/UIView+Dismiss.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// UIView+Dismiss.swift -// Custom Transitions -// -// Created by Aaron Tredrea on 27/11/2018. -// Copyright © 2018 Stanwood GmbH. All rights reserved. -// - -import Foundation - - -@objc protocol DismissView { - - func dismiss() - @objc optional func dismissViewAnimating() - @objc optional func dismissViewCompleted() -} - -extension DismissView where Self: UIView { - - func dismiss(with duration: TimeInterval, shouldDismissSelf dismiss: Bool = true){ - - UIView.animate(withDuration: duration, animations: { - - self.dismissViewAnimating?() - - self.alpha = .clear - self.transform = .shrink - self.layoutIfNeeded() - - }) { (complete) in - - if dismiss { - - self.removeFromSuperview() - } - - self.dismissViewCompleted?() - } - } -}