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 dba9c1b..79284a4 100644 --- a/StanwoodCore/Extensions/String+Extension.swift +++ b/StanwoodCore/Extensions/String+Extension.swift @@ -63,7 +63,47 @@ public extension String { return "tel://\(self)" } - /// Convert CamelCase to snake_case + 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 [] + } + } + /// Convert CamelCase to snake_case public func snakeCased() -> String? { let pattern = "([a-z0-9])([A-Z])" let regex = try? NSRegularExpression(pattern: pattern, options: []) @@ -72,3 +112,10 @@ public extension String { return regex?.stringByReplacingMatches(in: camelCaseString, options: [], range: range, withTemplate: "$1_$2").lowercased() } } + + +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 d45d6ed..67a9d05 100644 --- a/StanwoodCore/Extensions/UIApplication+Extension.swift +++ b/StanwoodCore/Extensions/UIApplication+Extension.swift @@ -75,4 +75,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+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 + } +}