Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c36078cda2 | |||
| b78c82194f | |||
| a112473a04 | |||
| c1b135a30a | |||
| c1a7622a26 | |||
| c3421432b0 | |||
| 99353d3610 | |||
| 3bee6898e0 | |||
| 587790b1db | |||
| 2698cfe23d | |||
| 42b82886ca | |||
| 7af77b2596 | |||
| 7c663e7760 | |||
| f561e94110 | |||
| 3bf06ae2b3 | |||
| a6d5e6f97d | |||
| 0f20afb87c | |||
| 2368747150 | |||
| 32a010e1ca | |||
| fce0efaffa | |||
| 1500c272b1 | |||
| ba859e3646 | |||
| 01b8702c4a | |||
| ec809ee1c5 | |||
| a5f41ab3d6 | |||
| 58554f1449 | |||
| 8c1b6e2c8d | |||
| 724635e387 | |||
| dfc0fb8e52 | |||
| 50c9ab4104 | |||
| bb0821d2cc | |||
| ae29eba5b9 | |||
| 0c98c3c699 | |||
| 4ecc8e96d8 | |||
| fac454f41d | |||
| ae68a1be5d | |||
| c111a20c86 | |||
| cc1b1ac604 | |||
| 265f77dd5e | |||
| aacf153d0e | |||
| 5b8fdf6590 | |||
| 883ade3052 | |||
| 44837098ea | |||
| 42063749ed | |||
| 464df2eb5c | |||
| 6f48df5761 | |||
| e331793afc | |||
| 82152b698a | |||
| 75eaaec598 | |||
| d2cc2e424c | |||
| 0ac35b4494 | |||
| 6c2600dea8 | |||
| 26d7422216 | |||
| a4a02b598f | |||
| 0309bb2e0e | |||
| 4e0507f132 | |||
| 8e33ff7614 | |||
| b7ab9e0327 | |||
| 4129f23d00 | |||
| 91b01d83d9 | |||
| 4eda691bd0 | |||
| 0889352299 | |||
| 34f267d43c | |||
| 425f8ec1ca | |||
| 25ea7eab4c | |||
| 16f060820f | |||
| 4c8faf7695 | |||
| 5b843582d4 | |||
| 8c58dbd2a3 | |||
| 366f6dffd1 | |||
| 0141560c98 | |||
| 900d4cbbc9 | |||
| dd8137b640 | |||
| 0118f80729 | |||
| d0412a4285 |
File diff suppressed because it is too large
Load Diff
+7
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:stork-controller.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
Binary file not shown.
+14
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>stork-controller.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,44 @@
|
||||
import UIKit
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
self.launch(rootViewController: Controller())
|
||||
return true
|
||||
}
|
||||
|
||||
func applicationWillResignActive(_ application: UIApplication) {
|
||||
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
||||
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
|
||||
}
|
||||
|
||||
func applicationDidEnterBackground(_ application: UIApplication) {
|
||||
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
|
||||
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
||||
}
|
||||
|
||||
func applicationWillEnterForeground(_ application: UIApplication) {
|
||||
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
|
||||
}
|
||||
|
||||
func applicationDidBecomeActive(_ application: UIApplication) {
|
||||
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ application: UIApplication) {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
}
|
||||
|
||||
func launch(rootViewController: UIViewController) {
|
||||
let frame = UIScreen.main.bounds
|
||||
self.window = UIWindow(frame: frame)
|
||||
self.window!.rootViewController = rootViewController
|
||||
self.window!.makeKeyAndVisible()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "83.5x83.5",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ios-marketing",
|
||||
"size" : "1024x1024",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,43 @@
|
||||
import UIKit
|
||||
|
||||
class Controller: UIViewController {
|
||||
|
||||
var presentControllerButton = UIButton.init(type: UIButton.ButtonType.system)
|
||||
var presentTableControllerButton = UIButton.init(type: UIButton.ButtonType.system)
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.view.backgroundColor = UIColor.white
|
||||
|
||||
self.presentControllerButton.setTitle("Show ViewController", for: .normal)
|
||||
self.presentControllerButton.addTarget(self, action: #selector(self.presentModalViewController), for: .touchUpInside)
|
||||
self.presentControllerButton.sizeToFit()
|
||||
self.presentControllerButton.center.x = self.view.frame.width / 2
|
||||
self.presentControllerButton.center.y = self.view.frame.height / 4 * 3
|
||||
self.view.addSubview(self.presentControllerButton)
|
||||
|
||||
self.presentTableControllerButton.setTitle("Show TableController", for: .normal)
|
||||
self.presentTableControllerButton.addTarget(self, action: #selector(self.presentModalTableViewController), for: .touchUpInside)
|
||||
self.presentTableControllerButton.sizeToFit()
|
||||
self.presentTableControllerButton.center.x = self.view.frame.width / 2
|
||||
self.presentTableControllerButton.frame.origin.y = self.presentControllerButton.frame.bottomYPosition + 10
|
||||
self.view.addSubview(self.presentTableControllerButton)
|
||||
}
|
||||
|
||||
@objc func presentModalViewController() {
|
||||
let modal = ModalViewController()
|
||||
let transitionDelegate = SPStorkTransitioningDelegate()
|
||||
modal.transitioningDelegate = transitionDelegate
|
||||
modal.modalPresentationStyle = .custom
|
||||
present(modal, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc func presentModalTableViewController() {
|
||||
let modal = ModalTableViewController()
|
||||
let transitionDelegate = SPStorkTransitioningDelegate()
|
||||
modal.transitioningDelegate = transitionDelegate
|
||||
modal.modalPresentationStyle = .custom
|
||||
present(modal, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public struct SPStorkController {
|
||||
|
||||
static public func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if let controller = self.controller(for: scrollView) {
|
||||
if let presentationController = controller.presentationController as? SPStorkPresentationController {
|
||||
let translation = -(scrollView.contentOffset.y + scrollView.contentInset.top)
|
||||
if translation >= 0 {
|
||||
scrollView.subviews.forEach {
|
||||
$0.transform = CGAffineTransform(translationX: 0, y: -translation)
|
||||
}
|
||||
if presentationController.pan?.state != UIGestureRecognizer.State.changed {
|
||||
presentationController.scrollViewDidScroll(translation)
|
||||
}
|
||||
} else {
|
||||
presentationController.scrollViewDidScroll(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public func updatePresentingController(parent controller: UIViewController) {
|
||||
if let presentationController = controller.presentedViewController?.presentationController as? SPStorkPresentationController {
|
||||
presentationController.updatePresentingController()
|
||||
}
|
||||
}
|
||||
|
||||
static public func updatePresentingController(modal controller: UIViewController) {
|
||||
if let presentationController = controller.presentationController as? SPStorkPresentationController {
|
||||
presentationController.updatePresentingController()
|
||||
}
|
||||
}
|
||||
|
||||
static private func controller(for view: UIView) -> UIViewController? {
|
||||
var nextResponder = view.next
|
||||
while nextResponder != nil && !(nextResponder! is UIViewController) {
|
||||
nextResponder = nextResponder!.next
|
||||
}
|
||||
return nextResponder as? UIViewController
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
var isPresentedAsStork: Bool {
|
||||
return transitioningDelegate is SPStorkTransitioningDelegate
|
||||
&& modalPresentationStyle == .custom
|
||||
&& presentingViewController != nil
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
final class SPStorkDismissingAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
|
||||
guard let presentedViewController = transitionContext.viewController(forKey: .from) else {
|
||||
return
|
||||
}
|
||||
|
||||
let containerView = transitionContext.containerView
|
||||
let offscreenFrame = CGRect(x: 0, y: containerView.bounds.height, width: containerView.bounds.width, height: containerView.bounds.height)
|
||||
|
||||
UIView.animate(
|
||||
withDuration: transitionDuration(using: transitionContext),
|
||||
delay: 0,
|
||||
usingSpringWithDamping: 1,
|
||||
initialSpringVelocity: 1,
|
||||
options: .curveEaseIn,
|
||||
animations: {
|
||||
presentedViewController.view.frame = offscreenFrame
|
||||
}) { finished in
|
||||
transitionContext.completeTransition(finished)
|
||||
}
|
||||
}
|
||||
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
return 0.6
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPStorkIndicatorView: UIView {
|
||||
|
||||
var style: Style = .line {
|
||||
didSet {
|
||||
switch self.style {
|
||||
case .line:
|
||||
self.animate {
|
||||
self.leftView.transform = .identity
|
||||
self.rightView.transform = .identity
|
||||
}
|
||||
case .arrow:
|
||||
self.animate {
|
||||
let angle = CGFloat(20 * Float.pi / 180)
|
||||
self.leftView.transform = CGAffineTransform.init(rotationAngle: angle)
|
||||
self.rightView.transform = CGAffineTransform.init(rotationAngle: -angle)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private var leftView: UIView = UIView()
|
||||
private var rightView: UIView = UIView()
|
||||
|
||||
init() {
|
||||
super.init(frame: .zero)
|
||||
self.backgroundColor = UIColor.clear
|
||||
self.addSubview(self.leftView)
|
||||
self.addSubview(self.rightView)
|
||||
self.leftView.backgroundColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
|
||||
self.rightView.backgroundColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func sizeToFit() {
|
||||
super.sizeToFit()
|
||||
self.frame = CGRect.init(x: self.frame.origin.x, y: self.frame.origin.y, width: 36, height: 13)
|
||||
|
||||
let height: CGFloat = 5
|
||||
let correction = height / 2
|
||||
|
||||
self.leftView.frame = CGRect.init(x: 0, y: 0, width: self.frame.width / 2 + correction, height: height)
|
||||
self.leftView.center.y = self.frame.height / 2
|
||||
self.leftView.layer.cornerRadius = min(self.leftView.frame.width, self.leftView.frame.height) / 2
|
||||
|
||||
self.rightView.frame = CGRect.init(x: self.frame.width / 2 - correction, y: 0, width: self.frame.width / 2 + correction, height: height)
|
||||
self.rightView.center.y = self.frame.height / 2
|
||||
self.rightView.layer.cornerRadius = min(self.leftView.frame.width, self.leftView.frame.height) / 2
|
||||
}
|
||||
|
||||
private func animate(animations: @escaping (() -> Void)) {
|
||||
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.beginFromCurrentState, .curveEaseOut], animations: {
|
||||
animations()
|
||||
})
|
||||
}
|
||||
|
||||
enum Style {
|
||||
case arrow
|
||||
case line
|
||||
}
|
||||
}
|
||||
+423
@@ -0,0 +1,423 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPStorkPresentationController: UIPresentationController, UIGestureRecognizerDelegate {
|
||||
|
||||
var isSwipeToDismissEnabled: Bool = true
|
||||
var isTapAroundToDismissEnabled: Bool = true
|
||||
var showIndicator: Bool = true
|
||||
var customHeight: CGFloat? = nil
|
||||
var transitioningDelegate: SPStorkTransitioningDelegate?
|
||||
|
||||
var pan: UIPanGestureRecognizer?
|
||||
var tap: UITapGestureRecognizer?
|
||||
|
||||
private var indicatorView = SPStorkIndicatorView()
|
||||
private var gradeView: UIView = UIView()
|
||||
private let snapshotViewContainer = UIView()
|
||||
private var snapshotView: UIView?
|
||||
private let backgroundView = UIView()
|
||||
|
||||
private var snapshotViewTopConstraint: NSLayoutConstraint?
|
||||
private var snapshotViewWidthConstraint: NSLayoutConstraint?
|
||||
private var snapshotViewAspectRatioConstraint: NSLayoutConstraint?
|
||||
private var workGester: Bool = false
|
||||
private var startDismissing: Bool = false
|
||||
|
||||
private var topSpace: CGFloat {
|
||||
let statusBarHeight: CGFloat = UIApplication.shared.statusBarFrame.height
|
||||
return (statusBarHeight < 25) ? 30 : statusBarHeight
|
||||
}
|
||||
|
||||
private var alpha: CGFloat {
|
||||
return 0.51
|
||||
}
|
||||
|
||||
private var cornerRadius: CGFloat {
|
||||
return 10
|
||||
}
|
||||
|
||||
private var scaleForPresentingView: CGFloat {
|
||||
guard let containerView = containerView else { return 0 }
|
||||
let factor = 1 - (self.topSpace * 2 / containerView.frame.height)
|
||||
return factor
|
||||
}
|
||||
|
||||
override var frameOfPresentedViewInContainerView: CGRect {
|
||||
guard let containerView = containerView else { return .zero }
|
||||
let additionTranslate = containerView.bounds.height - (self.customHeight ?? containerView.bounds.height)
|
||||
let yOffset: CGFloat = self.topSpace + 13 + additionTranslate
|
||||
return CGRect(x: 0, y: yOffset, width: containerView.bounds.width, height: containerView.bounds.height - yOffset)
|
||||
}
|
||||
|
||||
override func presentationTransitionWillBegin() {
|
||||
super.presentationTransitionWillBegin()
|
||||
|
||||
guard let containerView = self.containerView, let presentedView = self.presentedView, let window = containerView.window else { return }
|
||||
|
||||
if self.showIndicator {
|
||||
presentedView.addSubview(self.indicatorView)
|
||||
}
|
||||
self.updateLayoutIndicator()
|
||||
self.indicatorView.style = .arrow
|
||||
self.gradeView.alpha = 0
|
||||
|
||||
let initialFrame: CGRect = presentingViewController.isPresentedAsStork ? presentingViewController.view.frame : containerView.bounds
|
||||
|
||||
containerView.insertSubview(self.snapshotViewContainer, belowSubview: presentedViewController.view)
|
||||
self.snapshotViewContainer.frame = initialFrame
|
||||
self.updateSnapshot()
|
||||
self.snapshotView?.layer.cornerRadius = 0
|
||||
self.backgroundView.backgroundColor = UIColor.black
|
||||
self.backgroundView.translatesAutoresizingMaskIntoConstraints = false
|
||||
containerView.insertSubview(self.backgroundView, belowSubview: self.snapshotViewContainer)
|
||||
NSLayoutConstraint.activate([
|
||||
self.backgroundView.topAnchor.constraint(equalTo: window.topAnchor),
|
||||
self.backgroundView.leftAnchor.constraint(equalTo: window.leftAnchor),
|
||||
self.backgroundView.rightAnchor.constraint(equalTo: window.rightAnchor),
|
||||
self.backgroundView.bottomAnchor.constraint(equalTo: window.bottomAnchor)
|
||||
])
|
||||
|
||||
let transformForSnapshotView = CGAffineTransform.identity
|
||||
.translatedBy(x: 0, y: -snapshotViewContainer.frame.origin.y)
|
||||
.translatedBy(x: 0, y: self.topSpace)
|
||||
.translatedBy(x: 0, y: -snapshotViewContainer.frame.height / 2)
|
||||
.scaledBy(x: scaleForPresentingView, y: scaleForPresentingView)
|
||||
.translatedBy(x: 0, y: snapshotViewContainer.frame.height / 2)
|
||||
|
||||
self.addCornerRadiusAnimation(for: self.snapshotView, cornerRadius: self.cornerRadius, duration: 0.6)
|
||||
self.snapshotView?.layer.masksToBounds = true
|
||||
if #available(iOS 11.0, *) {
|
||||
presentedView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
}
|
||||
presentedView.layer.cornerRadius = self.cornerRadius
|
||||
presentedView.layer.masksToBounds = true
|
||||
|
||||
var rootSnapshotView: UIView?
|
||||
var rootSnapshotRoundedView: UIView?
|
||||
|
||||
if presentingViewController.isPresentedAsStork {
|
||||
guard let rootController = presentingViewController.presentingViewController, let snapshotView = rootController.view.snapshotView(afterScreenUpdates: false) else { return }
|
||||
|
||||
containerView.insertSubview(snapshotView, aboveSubview: self.backgroundView)
|
||||
snapshotView.frame = initialFrame
|
||||
snapshotView.transform = transformForSnapshotView
|
||||
snapshotView.alpha = self.alpha
|
||||
snapshotView.layer.cornerRadius = self.cornerRadius
|
||||
snapshotView.layer.masksToBounds = true
|
||||
rootSnapshotView = snapshotView
|
||||
|
||||
let snapshotRoundedView = UIView()
|
||||
snapshotRoundedView.layer.cornerRadius = self.cornerRadius
|
||||
snapshotRoundedView.layer.masksToBounds = true
|
||||
containerView.insertSubview(snapshotRoundedView, aboveSubview: snapshotView)
|
||||
snapshotRoundedView.frame = initialFrame
|
||||
snapshotRoundedView.transform = transformForSnapshotView
|
||||
rootSnapshotRoundedView = snapshotRoundedView
|
||||
}
|
||||
|
||||
presentedViewController.transitionCoordinator?.animate(
|
||||
alongsideTransition: { [weak self] context in
|
||||
guard let `self` = self else { return }
|
||||
self.snapshotView?.transform = transformForSnapshotView
|
||||
self.gradeView.alpha = self.alpha
|
||||
}, completion: { _ in
|
||||
self.snapshotView?.transform = .identity
|
||||
rootSnapshotView?.removeFromSuperview()
|
||||
rootSnapshotRoundedView?.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
|
||||
override func presentationTransitionDidEnd(_ completed: Bool) {
|
||||
super.presentationTransitionDidEnd(completed)
|
||||
guard let containerView = containerView else { return }
|
||||
self.updateSnapshot()
|
||||
self.presentedViewController.view.frame = self.frameOfPresentedViewInContainerView
|
||||
self.snapshotViewContainer.transform = .identity
|
||||
self.snapshotViewContainer.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.snapshotViewContainer.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
|
||||
self.updateSnapshotAspectRatio()
|
||||
|
||||
if self.isTapAroundToDismissEnabled {
|
||||
self.tap = UITapGestureRecognizer.init(target: self, action: #selector(self.handleTap))
|
||||
self.tap?.cancelsTouchesInView = false
|
||||
self.snapshotViewContainer.addGestureRecognizer(self.tap!)
|
||||
}
|
||||
|
||||
if self.isSwipeToDismissEnabled {
|
||||
self.pan = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan))
|
||||
self.pan!.delegate = self
|
||||
self.pan!.maximumNumberOfTouches = 1
|
||||
self.pan!.cancelsTouchesInView = false
|
||||
self.presentedViewController.view.addGestureRecognizer(self.pan!)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func handleTap() {
|
||||
self.presentedViewController.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
override func dismissalTransitionWillBegin() {
|
||||
super.dismissalTransitionWillBegin()
|
||||
guard let containerView = containerView else { return }
|
||||
self.startDismissing = true
|
||||
|
||||
let initialFrame: CGRect = presentingViewController.isPresentedAsStork ? presentingViewController.view.frame : containerView.bounds
|
||||
|
||||
let initialTransform = CGAffineTransform.identity
|
||||
.translatedBy(x: 0, y: -initialFrame.origin.y)
|
||||
.translatedBy(x: 0, y: self.topSpace)
|
||||
.translatedBy(x: 0, y: -initialFrame.height / 2)
|
||||
.scaledBy(x: scaleForPresentingView, y: scaleForPresentingView)
|
||||
.translatedBy(x: 0, y: initialFrame.height / 2)
|
||||
|
||||
self.snapshotViewTopConstraint?.isActive = false
|
||||
self.snapshotViewWidthConstraint?.isActive = false
|
||||
self.snapshotViewAspectRatioConstraint?.isActive = false
|
||||
self.snapshotViewContainer.translatesAutoresizingMaskIntoConstraints = true
|
||||
self.snapshotViewContainer.frame = initialFrame
|
||||
self.snapshotViewContainer.transform = initialTransform
|
||||
|
||||
let finalCornerRadius = presentingViewController.isPresentedAsStork ? self.cornerRadius : 0
|
||||
let finalTransform: CGAffineTransform = .identity
|
||||
|
||||
self.addCornerRadiusAnimation(for: self.snapshotView, cornerRadius: finalCornerRadius, duration: 0.6)
|
||||
|
||||
var rootSnapshotView: UIView?
|
||||
var rootSnapshotRoundedView: UIView?
|
||||
|
||||
if presentingViewController.isPresentedAsStork {
|
||||
guard let rootController = presentingViewController.presentingViewController, let snapshotView = rootController.view.snapshotView(afterScreenUpdates: false) else { return }
|
||||
|
||||
containerView.insertSubview(snapshotView, aboveSubview: backgroundView)
|
||||
snapshotView.frame = initialFrame
|
||||
snapshotView.transform = initialTransform
|
||||
rootSnapshotView = snapshotView
|
||||
snapshotView.layer.cornerRadius = self.cornerRadius
|
||||
snapshotView.layer.masksToBounds = true
|
||||
|
||||
let snapshotRoundedView = UIView()
|
||||
snapshotRoundedView.layer.cornerRadius = self.cornerRadius
|
||||
snapshotRoundedView.layer.masksToBounds = true
|
||||
snapshotRoundedView.backgroundColor = UIColor.black.withAlphaComponent(1 - self.alpha)
|
||||
containerView.insertSubview(snapshotRoundedView, aboveSubview: snapshotView)
|
||||
snapshotRoundedView.frame = initialFrame
|
||||
snapshotRoundedView.transform = initialTransform
|
||||
rootSnapshotRoundedView = snapshotRoundedView
|
||||
}
|
||||
|
||||
presentedViewController.transitionCoordinator?.animate(
|
||||
alongsideTransition: { [weak self] context in
|
||||
guard let `self` = self else { return }
|
||||
self.snapshotView?.transform = .identity
|
||||
self.snapshotViewContainer.transform = finalTransform
|
||||
self.gradeView.alpha = 0
|
||||
}, completion: { _ in
|
||||
rootSnapshotView?.removeFromSuperview()
|
||||
rootSnapshotRoundedView?.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
|
||||
override func dismissalTransitionDidEnd(_ completed: Bool) {
|
||||
super.dismissalTransitionDidEnd(completed)
|
||||
guard let containerView = containerView else { return }
|
||||
|
||||
self.backgroundView.removeFromSuperview()
|
||||
self.snapshotView?.removeFromSuperview()
|
||||
self.snapshotViewContainer.removeFromSuperview()
|
||||
|
||||
let offscreenFrame = CGRect(x: 0, y: containerView.bounds.height, width: containerView.bounds.width, height: containerView.bounds.height)
|
||||
presentedViewController.view.frame = offscreenFrame
|
||||
presentedViewController.view.transform = .identity
|
||||
}
|
||||
}
|
||||
|
||||
extension SPStorkPresentationController {
|
||||
|
||||
@objc func handlePan(gestureRecognizer: UIPanGestureRecognizer) {
|
||||
guard gestureRecognizer.isEqual(pan), self.isSwipeToDismissEnabled else { return }
|
||||
|
||||
switch gestureRecognizer.state {
|
||||
case .began:
|
||||
self.workGester = true
|
||||
self.indicatorView.style = .line
|
||||
self.presentingViewController.view.layer.removeAllAnimations()
|
||||
gestureRecognizer.setTranslation(CGPoint(x: 0, y: 0), in: containerView)
|
||||
case .changed:
|
||||
self.workGester = true
|
||||
if self.isSwipeToDismissEnabled {
|
||||
let translation = gestureRecognizer.translation(in: presentedView)
|
||||
self.updatePresentedViewForTranslation(inVerticalDirection: translation.y)
|
||||
} else {
|
||||
gestureRecognizer.setTranslation(.zero, in: presentedView)
|
||||
}
|
||||
case .ended:
|
||||
self.workGester = false
|
||||
let translation = gestureRecognizer.translation(in: presentedView).y
|
||||
if translation >= 240 {
|
||||
presentedViewController.dismiss(animated: true, completion: nil)
|
||||
} else {
|
||||
self.indicatorView.style = .arrow
|
||||
UIView.animate(
|
||||
withDuration: 0.6,
|
||||
delay: 0,
|
||||
usingSpringWithDamping: 1,
|
||||
initialSpringVelocity: 1,
|
||||
options: [.curveEaseOut, .allowUserInteraction],
|
||||
animations: {
|
||||
self.snapshotView?.transform = .identity
|
||||
self.presentedView?.transform = .identity
|
||||
self.gradeView.alpha = self.alpha
|
||||
})
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ translation: CGFloat) {
|
||||
if !self.workGester {
|
||||
self.updatePresentedViewForTranslation(inVerticalDirection: translation)
|
||||
}
|
||||
}
|
||||
|
||||
func updatePresentingController() {
|
||||
if self.startDismissing { return }
|
||||
self.updateSnapshot()
|
||||
}
|
||||
|
||||
private func updatePresentedViewForTranslation(inVerticalDirection translation: CGFloat) {
|
||||
if self.startDismissing { return }
|
||||
|
||||
let elasticThreshold: CGFloat = 120
|
||||
let translationFactor: CGFloat = 1 / 2
|
||||
|
||||
if translation >= 0 {
|
||||
let translationForModal: CGFloat = {
|
||||
if translation >= elasticThreshold {
|
||||
let frictionLength = translation - elasticThreshold
|
||||
let frictionTranslation = 30 * atan(frictionLength / 120) + frictionLength / 10
|
||||
return frictionTranslation + (elasticThreshold * translationFactor)
|
||||
} else {
|
||||
return translation * translationFactor
|
||||
}
|
||||
}()
|
||||
|
||||
self.presentedView?.transform = CGAffineTransform(translationX: 0, y: translationForModal)
|
||||
|
||||
let factor = 1 + (translationForModal / 6000)
|
||||
self.snapshotView?.transform = CGAffineTransform.init(scaleX: factor, y: factor)
|
||||
self.gradeView.alpha = self.alpha - ((factor - 1) * 15)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SPStorkPresentationController {
|
||||
|
||||
override func containerViewWillLayoutSubviews() {
|
||||
super.containerViewWillLayoutSubviews()
|
||||
guard let containerView = containerView else { return }
|
||||
self.updateSnapshotAspectRatio()
|
||||
if presentedViewController.view.isDescendant(of: containerView) {
|
||||
UIView.animate(withDuration: 0.1) { [weak self] in
|
||||
guard let `self` = self else { return }
|
||||
self.presentedViewController.view.frame = self.frameOfPresentedViewInContainerView
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
coordinator.animate(alongsideTransition: { contex in
|
||||
self.updateLayoutIndicator()
|
||||
}, completion: { [weak self] _ in
|
||||
self?.updateSnapshotAspectRatio()
|
||||
self?.updateSnapshot()
|
||||
})
|
||||
}
|
||||
|
||||
private func updateLayoutIndicator() {
|
||||
guard let presentedView = self.presentedView else { return }
|
||||
self.indicatorView.style = .line
|
||||
self.indicatorView.sizeToFit()
|
||||
self.indicatorView.frame.origin.y = 12
|
||||
self.indicatorView.center.x = presentedView.frame.width / 2
|
||||
}
|
||||
|
||||
private func updateSnapshot() {
|
||||
guard let currentSnapshotView = presentingViewController.view.snapshotView(afterScreenUpdates: true) else { return }
|
||||
self.snapshotView?.removeFromSuperview()
|
||||
self.snapshotViewContainer.addSubview(currentSnapshotView)
|
||||
self.constraints(view: currentSnapshotView, to: self.snapshotViewContainer)
|
||||
self.snapshotView = currentSnapshotView
|
||||
self.snapshotView?.layer.cornerRadius = self.cornerRadius
|
||||
self.snapshotView?.layer.masksToBounds = true
|
||||
if #available(iOS 11.0, *) {
|
||||
snapshotView?.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
}
|
||||
self.gradeView.removeFromSuperview()
|
||||
self.gradeView.backgroundColor = UIColor.black
|
||||
self.snapshotView!.addSubview(self.gradeView)
|
||||
self.constraints(view: self.gradeView, to: self.snapshotView!)
|
||||
}
|
||||
|
||||
private func updateSnapshotAspectRatio() {
|
||||
guard let containerView = containerView, snapshotViewContainer.translatesAutoresizingMaskIntoConstraints == false else { return }
|
||||
|
||||
self.snapshotViewTopConstraint?.isActive = false
|
||||
self.snapshotViewWidthConstraint?.isActive = false
|
||||
self.snapshotViewAspectRatioConstraint?.isActive = false
|
||||
|
||||
let snapshotReferenceSize = presentingViewController.view.frame.size
|
||||
let aspectRatio = snapshotReferenceSize.width / snapshotReferenceSize.height
|
||||
|
||||
self.snapshotViewTopConstraint = snapshotViewContainer.topAnchor.constraint(equalTo: containerView.topAnchor, constant: self.topSpace)
|
||||
self.snapshotViewWidthConstraint = snapshotViewContainer.widthAnchor.constraint(equalTo: containerView.widthAnchor, multiplier: scaleForPresentingView)
|
||||
self.snapshotViewAspectRatioConstraint = snapshotViewContainer.widthAnchor.constraint(equalTo: snapshotViewContainer.heightAnchor, multiplier: aspectRatio)
|
||||
|
||||
self.snapshotViewTopConstraint?.isActive = true
|
||||
self.snapshotViewWidthConstraint?.isActive = true
|
||||
self.snapshotViewAspectRatioConstraint?.isActive = true
|
||||
}
|
||||
|
||||
private func constraints(view: UIView, to superView: UIView) {
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
view.topAnchor.constraint(equalTo: superView.topAnchor),
|
||||
view.leftAnchor.constraint(equalTo: superView.leftAnchor),
|
||||
view.rightAnchor.constraint(equalTo: superView.rightAnchor),
|
||||
view.bottomAnchor.constraint(equalTo: superView.bottomAnchor)
|
||||
])
|
||||
}
|
||||
|
||||
private func addCornerRadiusAnimation(for view: UIView?, cornerRadius: CGFloat, duration: CFTimeInterval) {
|
||||
guard let view = view else { return }
|
||||
let animation = CABasicAnimation(keyPath:"cornerRadius")
|
||||
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
|
||||
animation.fromValue = view.layer.cornerRadius
|
||||
animation.toValue = cornerRadius
|
||||
animation.duration = duration
|
||||
view.layer.add(animation, forKey: "cornerRadius")
|
||||
view.layer.cornerRadius = cornerRadius
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
final class SPStorkPresentingAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
|
||||
guard let presentedViewController = transitionContext.viewController(forKey: .to) else {
|
||||
return
|
||||
}
|
||||
|
||||
let containerView = transitionContext.containerView
|
||||
containerView.addSubview(presentedViewController.view)
|
||||
presentedViewController.view.frame = CGRect(x: 0, y: containerView.bounds.height, width: containerView.bounds.width, height: containerView.bounds.height)
|
||||
|
||||
let finalFrameForPresentedView = transitionContext.finalFrame(for: presentedViewController)
|
||||
|
||||
UIView.animate(
|
||||
withDuration: transitionDuration(using: transitionContext),
|
||||
delay: 0,
|
||||
usingSpringWithDamping: 1,
|
||||
initialSpringVelocity: 1,
|
||||
options: .curveEaseOut,
|
||||
animations: {
|
||||
presentedViewController.view.frame = finalFrameForPresentedView
|
||||
}, completion: { finished in
|
||||
transitionContext.completeTransition(finished)
|
||||
})
|
||||
}
|
||||
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
return 0.6
|
||||
}
|
||||
}
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public final class SPStorkTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
|
||||
|
||||
public var isSwipeToDismissEnabled: Bool = true
|
||||
public var isTapAroundToDismissEnabled: Bool = true
|
||||
public var showIndicator: Bool = true
|
||||
public var customHeight: CGFloat? = nil
|
||||
|
||||
public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
|
||||
let controller = SPStorkPresentationController(presentedViewController: presented, presenting: presenting)
|
||||
controller.isSwipeToDismissEnabled = self.isSwipeToDismissEnabled
|
||||
controller.isTapAroundToDismissEnabled = self.isTapAroundToDismissEnabled
|
||||
controller.showIndicator = self.showIndicator
|
||||
controller.customHeight = self.customHeight
|
||||
controller.transitioningDelegate = self
|
||||
return controller
|
||||
}
|
||||
|
||||
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return SPStorkPresentingAnimationController()
|
||||
}
|
||||
|
||||
public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return SPStorkDismissingAnimationController()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPAnimation {
|
||||
|
||||
static func animate(_ duration: TimeInterval,
|
||||
animations: (() -> Void)!,
|
||||
delay: TimeInterval = 0,
|
||||
options: UIView.AnimationOptions = [],
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
|
||||
UIView.animate(
|
||||
withDuration: duration,
|
||||
delay: delay,
|
||||
options: options,
|
||||
animations: {
|
||||
animations()
|
||||
}, completion: { finished in
|
||||
completion()
|
||||
})
|
||||
}
|
||||
|
||||
static func animateWithRepeatition(_ duration: TimeInterval,
|
||||
animations: (() -> Void)!,
|
||||
delay: TimeInterval = 0,
|
||||
options: UIView.AnimationOptions = [],
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
|
||||
var optionsWithRepeatition = options
|
||||
optionsWithRepeatition.insert([.autoreverse, .repeat])
|
||||
|
||||
self.animate(
|
||||
duration,
|
||||
animations: {
|
||||
animations()
|
||||
},
|
||||
delay: delay,
|
||||
options: optionsWithRepeatition,
|
||||
withComplection: {
|
||||
completion()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPAnimationAlpha {
|
||||
|
||||
fileprivate static let durationListAnimation: TimeInterval = 0.45
|
||||
fileprivate static let coefLenthForTransition: CGFloat = 2.8
|
||||
fileprivate static let delayPerItem: TimeInterval = 0.09
|
||||
|
||||
static func hideList(_ duration: TimeInterval = durationListAnimation,
|
||||
views: [UIView],
|
||||
delayPerItem: TimeInterval = delayPerItem,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
|
||||
var del: Double = 0
|
||||
for view in views {
|
||||
delay(del, closure: {
|
||||
if (view == views.last) {
|
||||
SPAnimation.animate(duration, animations: {
|
||||
view.alpha = 0
|
||||
}, withComplection: {
|
||||
completion()
|
||||
})
|
||||
} else {
|
||||
SPAnimation.animate(duration, animations: {
|
||||
view.alpha = 0
|
||||
})
|
||||
}
|
||||
})
|
||||
del += delayPerItem
|
||||
}
|
||||
}
|
||||
|
||||
static func hideReverseList(_ duration: TimeInterval = durationListAnimation,
|
||||
views: [UIView],
|
||||
delayPerItem: TimeInterval = delayPerItem,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
|
||||
var del: Double = 0
|
||||
let reversedViews = views.reversed()
|
||||
for view in reversedViews {
|
||||
delay(del, closure: {
|
||||
if (view == views.last) {
|
||||
SPAnimation.animate(duration, animations: {
|
||||
view.alpha = 0
|
||||
}, withComplection: {
|
||||
completion()
|
||||
})
|
||||
} else {
|
||||
SPAnimation.animate(duration, animations: {
|
||||
view.alpha = 0
|
||||
})
|
||||
}
|
||||
})
|
||||
del += delayPerItem
|
||||
}
|
||||
}
|
||||
|
||||
static func showList(_ duration: TimeInterval = durationListAnimation,
|
||||
views: [UIView],
|
||||
delayPerItem: TimeInterval = delayPerItem,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
|
||||
var del: Double = 0
|
||||
for view in views {
|
||||
delay(del, closure: {
|
||||
if (view == views.last) {
|
||||
SPAnimation.animate(duration, animations: {
|
||||
view.alpha = 1
|
||||
}, withComplection: {
|
||||
completion()
|
||||
})
|
||||
} else {
|
||||
SPAnimation.animate(duration, animations: {
|
||||
view.alpha = 1
|
||||
})
|
||||
}
|
||||
})
|
||||
del += delayPerItem
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPAnimationSpring {
|
||||
|
||||
fileprivate static let spring: CGFloat = 1
|
||||
fileprivate static let velocity: CGFloat = 1
|
||||
|
||||
static func animate(_ duration: TimeInterval,
|
||||
animations: (() -> Void)!,
|
||||
delay: TimeInterval = 0,
|
||||
spring: CGFloat = spring,
|
||||
velocity: CGFloat = velocity,
|
||||
options: UIView.AnimationOptions = [],
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
|
||||
UIView.animate(
|
||||
withDuration: duration,
|
||||
delay: delay,
|
||||
usingSpringWithDamping: spring,
|
||||
initialSpringVelocity: velocity,
|
||||
options: options,
|
||||
animations: {
|
||||
animations()
|
||||
}, completion: { finished in
|
||||
completion()
|
||||
})
|
||||
}
|
||||
|
||||
static func animateWithRepeatition(_ duration: TimeInterval,
|
||||
animations: (() -> Void)!,
|
||||
delay: TimeInterval = 0,
|
||||
spring: CGFloat = spring,
|
||||
velocity: CGFloat = velocity,
|
||||
options: UIView.AnimationOptions = [],
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
|
||||
var optionsWithRepeatition = options
|
||||
optionsWithRepeatition.insert([.autoreverse, .repeat])
|
||||
|
||||
UIView.animate(
|
||||
withDuration: duration,
|
||||
delay: delay,
|
||||
usingSpringWithDamping: spring,
|
||||
initialSpringVelocity: velocity,
|
||||
options: optionsWithRepeatition,
|
||||
animations: {
|
||||
animations()
|
||||
}, completion: { finished in
|
||||
completion()
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPAnimationUpward {
|
||||
|
||||
fileprivate static let durationListAnimation: TimeInterval = 0.45
|
||||
fileprivate static let coefLenthForTransition: CGFloat = 2.8
|
||||
fileprivate static let delayPerItem: TimeInterval = 0.09
|
||||
|
||||
static func hide(_ duration: TimeInterval,
|
||||
view: UIView,
|
||||
delay: TimeInterval = 0,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
|
||||
var options: UIView.AnimationOptions = []
|
||||
options.insert(.curveEaseIn)
|
||||
|
||||
SPAnimationSpring.animate(
|
||||
duration, animations: {
|
||||
view.alpha = 0
|
||||
view.frame.origin.y = view.frame.origin.y + (-UIScreen.main.bounds.height / coefLenthForTransition)
|
||||
},
|
||||
delay: delay,
|
||||
options: options,
|
||||
withComplection: {
|
||||
completion()
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
static func hideList(_ duration: TimeInterval = durationListAnimation,
|
||||
views: [UIView],
|
||||
delayPerItem: TimeInterval = delayPerItem,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
|
||||
var del: Double = 0
|
||||
for view in views {
|
||||
delay(del, closure: {
|
||||
if (view == views.last) {
|
||||
SPAnimationUpward.hide(duration, view: view, withComplection: {
|
||||
completion()
|
||||
})
|
||||
} else {
|
||||
SPAnimationUpward.hide(duration, view: view)
|
||||
}
|
||||
})
|
||||
del += delayPerItem
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static func show(_ duration: TimeInterval,
|
||||
view: UIView,
|
||||
delay: TimeInterval = 0,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
|
||||
view.alpha = 0
|
||||
view.isHidden = false
|
||||
view.transform = CGAffineTransform(translationX: 0, y: UIScreen.main.bounds.height / coefLenthForTransition)
|
||||
|
||||
var options: UIView.AnimationOptions = []
|
||||
options.insert(.curveEaseOut)
|
||||
|
||||
SPAnimationSpring.animate(
|
||||
duration, animations: {
|
||||
view.alpha = 1
|
||||
view.transform = CGAffineTransform.identity
|
||||
},
|
||||
delay: delay,
|
||||
options: options,
|
||||
withComplection: {
|
||||
completion()
|
||||
})
|
||||
}
|
||||
|
||||
static func showList(_ duration: TimeInterval = durationListAnimation,
|
||||
views: [UIView],
|
||||
delayPerItem: TimeInterval = delayPerItem,
|
||||
options: UIView.AnimationOptions = [],
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
|
||||
var del: Double = 0
|
||||
for view in views {
|
||||
delay(del, closure: {
|
||||
if (view == views.last) {
|
||||
SPAnimationUpward.show(duration, view: view, withComplection: {
|
||||
completion()
|
||||
})
|
||||
} else {
|
||||
SPAnimationUpward.show(duration, view: view)
|
||||
}
|
||||
})
|
||||
del += delayPerItem
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
struct SPApp {
|
||||
|
||||
static var udid: String? {
|
||||
return UIDevice.current.identifierForVendor?.uuidString
|
||||
}
|
||||
|
||||
static var displayName: String? {
|
||||
return Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
|
||||
}
|
||||
|
||||
static var rootController: UIViewController? {
|
||||
return UIApplication.shared.keyWindow?.rootViewController
|
||||
}
|
||||
|
||||
static func set(rootController: UIViewController, animatable: Bool = true) {
|
||||
|
||||
rootController.view.frame = UIScreen.main.bounds
|
||||
|
||||
let replaceRootViewController = {
|
||||
UIApplication.shared.keyWindow?.rootViewController = rootController
|
||||
}
|
||||
|
||||
if animatable {
|
||||
UIView.transition(
|
||||
with: UIApplication.shared.keyWindow ?? UIWindow(),
|
||||
duration: 0.5,
|
||||
options: UIView.AnimationOptions.transitionCrossDissolve,
|
||||
animations: {
|
||||
replaceRootViewController()
|
||||
}, completion: nil)
|
||||
} else {
|
||||
replaceRootViewController()
|
||||
}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
import StoreKit
|
||||
|
||||
struct SPAppStore {
|
||||
|
||||
static func link(appID: String) -> String {
|
||||
return "https://itunes.apple.com/by/app/id" + appID
|
||||
}
|
||||
|
||||
static func open(appID: String) {
|
||||
if let url = URL(string: "itms-apps://itunes.apple.com/app/id\(appID)"),
|
||||
UIApplication.shared.canOpenURL(url) {
|
||||
if #available(iOS 10.0, *) {
|
||||
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
|
||||
} else {
|
||||
UIApplication.shared.openURL(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func requestReview(appID: String, force: Bool) {
|
||||
if force {
|
||||
if let url = URL(string: "itms-apps://itunes.apple.com/us/app/apple-store/id\(appID)?mt=8&action=write-review"),
|
||||
UIApplication.shared.canOpenURL(url) {
|
||||
if #available(iOS 10.0, *) {
|
||||
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
|
||||
} else {
|
||||
UIApplication.shared.openURL(url)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if #available(iOS 10.3, *) {
|
||||
SKStoreReviewController.requestReview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func isUpdateAvailable(completion: @escaping (Bool)->()) {
|
||||
|
||||
guard let info = Bundle.main.infoDictionary,
|
||||
let currentVersion = info["CFBundleShortVersionString"] as? String,
|
||||
let identifier = info["CFBundleIdentifier"] as? String,
|
||||
let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
|
||||
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
|
||||
do {
|
||||
if let error = error { throw error }
|
||||
guard let data = data else {
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
let json = try JSONSerialization.jsonObject(with: data, options: [.allowFragments]) as? [String: Any]
|
||||
guard let result = (json?["results"] as? [Any])?.first as? [String: Any], let version = result["version"] as? String else {
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
|
||||
let compareResult = currentVersion.compare(version, options: .numeric)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
completion(compareResult == .orderedAscending)
|
||||
}
|
||||
} catch {
|
||||
completion(false)
|
||||
}
|
||||
}
|
||||
task.resume()
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
extension NSRange {
|
||||
|
||||
func range(for str: String) -> Range<String.Index>? {
|
||||
guard location != NSNotFound else { return nil }
|
||||
|
||||
guard let fromUTFIndex = str.utf16.index(str.utf16.startIndex, offsetBy: location, limitedBy: str.utf16.endIndex) else { return nil }
|
||||
guard let toUTFIndex = str.utf16.index(fromUTFIndex, offsetBy: length, limitedBy: str.utf16.endIndex) else { return nil }
|
||||
guard let fromIndex = String.Index(fromUTFIndex, within: str) else { return nil }
|
||||
guard let toIndex = String.Index(toUTFIndex, within: str) else { return nil }
|
||||
|
||||
return fromIndex ..< toIndex
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
|
||||
func ranges(of string: String, options: CompareOptions = .literal) -> [Range<Index>] {
|
||||
var result: [Range<Index>] = []
|
||||
var start = startIndex
|
||||
while let range = range(of: string, options: options, range: start..<endIndex) {
|
||||
result.append(range)
|
||||
start = range.lowerBound < range.upperBound ? range.upperBound : index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func slices(from: String, to: String) -> [Substring] {
|
||||
let pattern = "(?<=" + from + ").*?(?=" + to + ")"
|
||||
|
||||
return ranges(of: pattern, options: .regularExpression)
|
||||
.map{ self[$0] }
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function inserted by Swift 4.2 migrator.
|
||||
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
|
||||
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
public struct SPAudio {
|
||||
|
||||
static func notStopBackgroundMusic() {
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category(rawValue: convertFromAVAudioSessionCategory(AVAudioSession.Category.ambient)), mode: AVAudioSession.Mode.default)
|
||||
try AVAudioSession.sharedInstance().setActive(true)
|
||||
} catch {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
// Helper function inserted by Swift 4.2 migrator.
|
||||
fileprivate func convertFromAVAudioSessionCategory(_ input: AVAudioSession.Category) -> String {
|
||||
return input.rawValue
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
public class SPAudioPlayer: NSObject, AVAudioPlayerDelegate {
|
||||
|
||||
fileprivate var player: AVAudioPlayer = AVAudioPlayer()
|
||||
fileprivate var endPlayingComplection: (()->())? = nil
|
||||
|
||||
func play(fileName: String, complection: (()->())? = nil) {
|
||||
self.endPlayingComplection?()
|
||||
self.player = AVAudioPlayer()
|
||||
let url = Bundle.main.url(forResource: fileName, withExtension: nil)
|
||||
if url == nil {
|
||||
self.endPlayingComplection?()
|
||||
return
|
||||
}
|
||||
do {
|
||||
self.player = try AVAudioPlayer(contentsOf: url!)
|
||||
player.volume = 1
|
||||
player.delegate = self
|
||||
player.prepareToPlay()
|
||||
player.play()
|
||||
self.endPlayingComplection = complection
|
||||
} catch let error as NSError {
|
||||
print(error.description)
|
||||
}
|
||||
}
|
||||
|
||||
func stop() {
|
||||
player.stop()
|
||||
}
|
||||
|
||||
public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
|
||||
self.endPlayingComplection?()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public struct SPBadge {
|
||||
|
||||
static func reset() {
|
||||
UIApplication.shared.applicationIconBadgeNumber = 0
|
||||
}
|
||||
|
||||
static var number: Int {
|
||||
get {
|
||||
return UIApplication.shared.applicationIconBadgeNumber
|
||||
}
|
||||
set {
|
||||
UIApplication.shared.applicationIconBadgeNumber = newValue
|
||||
}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
import LocalAuthentication
|
||||
|
||||
struct SPLocalAuthentication {
|
||||
|
||||
static var isEnable: Bool {
|
||||
let context = LAContext()
|
||||
var error: NSError?
|
||||
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
|
||||
return true
|
||||
} else {
|
||||
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func request(reason: String, complecton: @escaping (Bool)->()) {
|
||||
let context = LAContext()
|
||||
var error: NSError?
|
||||
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
|
||||
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, error in
|
||||
DispatchQueue.main.async { complecton(success) }
|
||||
}
|
||||
} else {
|
||||
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
|
||||
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) { success, error in
|
||||
DispatchQueue.main.async { complecton(success) }
|
||||
}
|
||||
} else {
|
||||
complecton(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
struct SPBufer {
|
||||
|
||||
static var text: String? {
|
||||
get {
|
||||
return UIPasteboard.general.string
|
||||
}
|
||||
set {
|
||||
UIPasteboard.general.string = newValue
|
||||
}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension SPCodeDraw {
|
||||
|
||||
public class AudioIconPack : NSObject {
|
||||
|
||||
@objc dynamic public class func drawPlay(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
context.saveGState()
|
||||
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 200, height: 200), target: targetFrame)
|
||||
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
|
||||
context.scaleBy(x: resizedFrame.width / 200, y: resizedFrame.height / 200)
|
||||
|
||||
let bezierPath = UIBezierPath()
|
||||
bezierPath.move(to: CGPoint(x: 177.85, y: 86.98))
|
||||
bezierPath.addLine(to: CGPoint(x: 54.26, y: 9.11))
|
||||
bezierPath.addCurve(to: CGPoint(x: 42.64, y: 7.3), controlPoint1: CGPoint(x: 50.61, y: 6.94), controlPoint2: CGPoint(x: 46.07, y: 7.3))
|
||||
bezierPath.addCurve(to: CGPoint(x: 29, y: 20.56), controlPoint1: CGPoint(x: 28.94, y: 7.3), controlPoint2: CGPoint(x: 29, y: 17.88))
|
||||
bezierPath.addLine(to: CGPoint(x: 29, y: 179.72))
|
||||
bezierPath.addCurve(to: CGPoint(x: 42.64, y: 192.98), controlPoint1: CGPoint(x: 29, y: 181.98), controlPoint2: CGPoint(x: 28.94, y: 192.98))
|
||||
bezierPath.addCurve(to: CGPoint(x: 54.26, y: 191.16), controlPoint1: CGPoint(x: 46.07, y: 192.98), controlPoint2: CGPoint(x: 50.61, y: 193.34))
|
||||
bezierPath.addLine(to: CGPoint(x: 177.85, y: 113.3))
|
||||
bezierPath.addCurve(to: CGPoint(x: 186.24, y: 100.14), controlPoint1: CGPoint(x: 187.99, y: 107.26), controlPoint2: CGPoint(x: 186.24, y: 100.14))
|
||||
bezierPath.addCurve(to: CGPoint(x: 177.85, y: 86.98), controlPoint1: CGPoint(x: 186.24, y: 100.14), controlPoint2: CGPoint(x: 187.99, y: 93.02))
|
||||
bezierPath.close()
|
||||
bezierPath.usesEvenOddFillRule = true
|
||||
fillColor.setFill()
|
||||
bezierPath.fill()
|
||||
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawPause(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
context.saveGState()
|
||||
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 200, height: 200), target: targetFrame)
|
||||
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
|
||||
context.scaleBy(x: resizedFrame.width / 200, y: resizedFrame.height / 200)
|
||||
|
||||
let bezierPath = UIBezierPath()
|
||||
bezierPath.move(to: CGPoint(x: 70.11, y: 6.86))
|
||||
bezierPath.addLine(to: CGPoint(x: 34.32, y: 6.86))
|
||||
bezierPath.addCurve(to: CGPoint(x: 20, y: 21.18), controlPoint1: CGPoint(x: 26.41, y: 6.86), controlPoint2: CGPoint(x: 20, y: 13.27))
|
||||
bezierPath.addLine(to: CGPoint(x: 20, y: 178.68))
|
||||
bezierPath.addCurve(to: CGPoint(x: 34.32, y: 193), controlPoint1: CGPoint(x: 20, y: 186.59), controlPoint2: CGPoint(x: 26.41, y: 193))
|
||||
bezierPath.addLine(to: CGPoint(x: 70.11, y: 193))
|
||||
bezierPath.addCurve(to: CGPoint(x: 84.43, y: 178.68), controlPoint1: CGPoint(x: 78.02, y: 193), controlPoint2: CGPoint(x: 84.43, y: 186.59))
|
||||
bezierPath.addLine(to: CGPoint(x: 84.43, y: 21.18))
|
||||
bezierPath.addCurve(to: CGPoint(x: 70.11, y: 6.86), controlPoint1: CGPoint(x: 84.43, y: 13.27), controlPoint2: CGPoint(x: 78.02, y: 6.86))
|
||||
bezierPath.close()
|
||||
bezierPath.move(to: CGPoint(x: 163.19, y: 6.86))
|
||||
bezierPath.addLine(to: CGPoint(x: 127.39, y: 6.86))
|
||||
bezierPath.addCurve(to: CGPoint(x: 113.07, y: 21.18), controlPoint1: CGPoint(x: 119.48, y: 6.86), controlPoint2: CGPoint(x: 113.07, y: 13.27))
|
||||
bezierPath.addLine(to: CGPoint(x: 113.07, y: 178.68))
|
||||
bezierPath.addCurve(to: CGPoint(x: 127.39, y: 193), controlPoint1: CGPoint(x: 113.07, y: 186.59), controlPoint2: CGPoint(x: 119.48, y: 193))
|
||||
bezierPath.addLine(to: CGPoint(x: 163.19, y: 193))
|
||||
bezierPath.addCurve(to: CGPoint(x: 177.5, y: 178.68), controlPoint1: CGPoint(x: 171.09, y: 193), controlPoint2: CGPoint(x: 177.5, y: 186.59))
|
||||
bezierPath.addLine(to: CGPoint(x: 177.5, y: 21.18))
|
||||
bezierPath.addCurve(to: CGPoint(x: 163.19, y: 6.86), controlPoint1: CGPoint(x: 177.5, y: 13.27), controlPoint2: CGPoint(x: 171.09, y: 6.86))
|
||||
bezierPath.close()
|
||||
bezierPath.usesEvenOddFillRule = true
|
||||
fillColor.setFill()
|
||||
bezierPath.fill()
|
||||
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawStop(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
context.saveGState()
|
||||
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 200, height: 200), target: targetFrame)
|
||||
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
|
||||
context.scaleBy(x: resizedFrame.width / 200, y: resizedFrame.height / 200)
|
||||
|
||||
let bezierPath = UIBezierPath()
|
||||
bezierPath.move(to: CGPoint(x: 177.82, y: 7.86))
|
||||
bezierPath.addLine(to: CGPoint(x: 20.32, y: 7.86))
|
||||
bezierPath.addCurve(to: CGPoint(x: 6, y: 22.18), controlPoint1: CGPoint(x: 12.41, y: 7.86), controlPoint2: CGPoint(x: 6, y: 14.27))
|
||||
bezierPath.addLine(to: CGPoint(x: 6, y: 179.68))
|
||||
bezierPath.addCurve(to: CGPoint(x: 20.32, y: 194), controlPoint1: CGPoint(x: 6, y: 187.59), controlPoint2: CGPoint(x: 12.41, y: 194))
|
||||
bezierPath.addLine(to: CGPoint(x: 177.82, y: 194))
|
||||
bezierPath.addCurve(to: CGPoint(x: 192.15, y: 179.68), controlPoint1: CGPoint(x: 185.73, y: 194), controlPoint2: CGPoint(x: 192.15, y: 187.59))
|
||||
bezierPath.addLine(to: CGPoint(x: 192.15, y: 22.18))
|
||||
bezierPath.addCurve(to: CGPoint(x: 177.82, y: 7.86), controlPoint1: CGPoint(x: 192.15, y: 14.27), controlPoint2: CGPoint(x: 185.73, y: 7.86))
|
||||
bezierPath.close()
|
||||
bezierPath.usesEvenOddFillRule = true
|
||||
fillColor.setFill()
|
||||
bezierPath.fill()
|
||||
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
@objc(StyleKitNameResizingBehavior)
|
||||
public enum ResizingBehavior: Int {
|
||||
case aspectFit /// The content is proportionally resized to fit into the target rectangle.
|
||||
case aspectFill /// The content is proportionally resized to completely fill the target rectangle.
|
||||
case stretch /// The content is stretched to match the entire target rectangle.
|
||||
case center /// The content is centered in the target rectangle, but it is NOT resized.
|
||||
|
||||
public func apply(rect: CGRect, target: CGRect) -> CGRect {
|
||||
if rect == target || target == CGRect.zero {
|
||||
return rect
|
||||
}
|
||||
|
||||
var scales = CGSize.zero
|
||||
scales.width = abs(target.width / rect.width)
|
||||
scales.height = abs(target.height / rect.height)
|
||||
|
||||
switch self {
|
||||
case .aspectFit:
|
||||
scales.width = min(scales.width, scales.height)
|
||||
scales.height = scales.width
|
||||
case .aspectFill:
|
||||
scales.width = max(scales.width, scales.height)
|
||||
scales.height = scales.width
|
||||
case .stretch:
|
||||
break
|
||||
case .center:
|
||||
scales.width = 1
|
||||
scales.height = 1
|
||||
}
|
||||
|
||||
var result = rect.standardized
|
||||
result.size.width *= scales.width
|
||||
result.size.height *= scales.height
|
||||
result.origin.x = target.minX + (target.width - result.width) / 2
|
||||
result.origin.y = target.minY + (target.height - result.height) / 2
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
private override init() {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
struct SPCodeDraw { private init(){} }
|
||||
@@ -0,0 +1,486 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension SPCodeDraw {
|
||||
|
||||
public class SocialIconPack : NSObject {
|
||||
|
||||
@objc dynamic public class func drawInstagram(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
|
||||
context.saveGState()
|
||||
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 40, height: 40), target: targetFrame)
|
||||
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
|
||||
context.scaleBy(x: resizedFrame.width / 40, y: resizedFrame.height / 40)
|
||||
|
||||
let bezierPath = UIBezierPath()
|
||||
bezierPath.move(to: CGPoint(x: 19.9, y: 0.99))
|
||||
bezierPath.addCurve(to: CGPoint(x: 12.12, y: 1.1), controlPoint1: CGPoint(x: 14.77, y: 0.99), controlPoint2: CGPoint(x: 14.13, y: 1.01))
|
||||
bezierPath.addCurve(to: CGPoint(x: 7.54, y: 1.98), controlPoint1: CGPoint(x: 10.11, y: 1.19), controlPoint2: CGPoint(x: 8.74, y: 1.51))
|
||||
bezierPath.addCurve(to: CGPoint(x: 4.19, y: 4.15), controlPoint1: CGPoint(x: 6.29, y: 2.46), controlPoint2: CGPoint(x: 5.24, y: 3.1))
|
||||
bezierPath.addCurve(to: CGPoint(x: 2.02, y: 7.5), controlPoint1: CGPoint(x: 3.14, y: 5.2), controlPoint2: CGPoint(x: 2.5, y: 6.26))
|
||||
bezierPath.addCurve(to: CGPoint(x: 1.14, y: 12.08), controlPoint1: CGPoint(x: 1.55, y: 8.7), controlPoint2: CGPoint(x: 1.23, y: 10.07))
|
||||
bezierPath.addCurve(to: CGPoint(x: 1.03, y: 19.86), controlPoint1: CGPoint(x: 1.05, y: 14.09), controlPoint2: CGPoint(x: 1.03, y: 14.73))
|
||||
bezierPath.addCurve(to: CGPoint(x: 1.14, y: 27.64), controlPoint1: CGPoint(x: 1.03, y: 24.98), controlPoint2: CGPoint(x: 1.05, y: 25.63))
|
||||
bezierPath.addCurve(to: CGPoint(x: 2.02, y: 32.22), controlPoint1: CGPoint(x: 1.23, y: 29.65), controlPoint2: CGPoint(x: 1.55, y: 31.02))
|
||||
bezierPath.addCurve(to: CGPoint(x: 4.19, y: 35.56), controlPoint1: CGPoint(x: 2.5, y: 33.46), controlPoint2: CGPoint(x: 3.14, y: 34.51))
|
||||
bezierPath.addCurve(to: CGPoint(x: 7.54, y: 37.74), controlPoint1: CGPoint(x: 5.24, y: 36.61), controlPoint2: CGPoint(x: 6.29, y: 37.26))
|
||||
bezierPath.addCurve(to: CGPoint(x: 12.12, y: 38.62), controlPoint1: CGPoint(x: 8.74, y: 38.21), controlPoint2: CGPoint(x: 10.11, y: 38.52))
|
||||
bezierPath.addCurve(to: CGPoint(x: 19.9, y: 38.73), controlPoint1: CGPoint(x: 14.13, y: 38.71), controlPoint2: CGPoint(x: 14.77, y: 38.73))
|
||||
bezierPath.addCurve(to: CGPoint(x: 27.68, y: 38.62), controlPoint1: CGPoint(x: 25.02, y: 38.73), controlPoint2: CGPoint(x: 25.67, y: 38.71))
|
||||
bezierPath.addCurve(to: CGPoint(x: 32.26, y: 37.74), controlPoint1: CGPoint(x: 29.69, y: 38.52), controlPoint2: CGPoint(x: 31.06, y: 38.21))
|
||||
bezierPath.addCurve(to: CGPoint(x: 35.6, y: 35.56), controlPoint1: CGPoint(x: 33.5, y: 37.26), controlPoint2: CGPoint(x: 34.55, y: 36.61))
|
||||
bezierPath.addCurve(to: CGPoint(x: 37.78, y: 32.22), controlPoint1: CGPoint(x: 36.65, y: 34.51), controlPoint2: CGPoint(x: 37.3, y: 33.46))
|
||||
bezierPath.addCurve(to: CGPoint(x: 38.66, y: 27.64), controlPoint1: CGPoint(x: 38.25, y: 31.02), controlPoint2: CGPoint(x: 38.56, y: 29.65))
|
||||
bezierPath.addCurve(to: CGPoint(x: 38.77, y: 19.86), controlPoint1: CGPoint(x: 38.75, y: 25.63), controlPoint2: CGPoint(x: 38.77, y: 24.98))
|
||||
bezierPath.addCurve(to: CGPoint(x: 38.66, y: 12.08), controlPoint1: CGPoint(x: 38.77, y: 14.73), controlPoint2: CGPoint(x: 38.75, y: 14.09))
|
||||
bezierPath.addCurve(to: CGPoint(x: 37.78, y: 7.5), controlPoint1: CGPoint(x: 38.56, y: 10.07), controlPoint2: CGPoint(x: 38.25, y: 8.7))
|
||||
bezierPath.addCurve(to: CGPoint(x: 35.6, y: 4.15), controlPoint1: CGPoint(x: 37.3, y: 6.26), controlPoint2: CGPoint(x: 36.65, y: 5.2))
|
||||
bezierPath.addCurve(to: CGPoint(x: 32.26, y: 1.98), controlPoint1: CGPoint(x: 34.55, y: 3.1), controlPoint2: CGPoint(x: 33.5, y: 2.46))
|
||||
bezierPath.addCurve(to: CGPoint(x: 27.68, y: 1.1), controlPoint1: CGPoint(x: 31.06, y: 1.51), controlPoint2: CGPoint(x: 29.69, y: 1.19))
|
||||
bezierPath.addCurve(to: CGPoint(x: 19.9, y: 0.99), controlPoint1: CGPoint(x: 25.67, y: 1.01), controlPoint2: CGPoint(x: 25.02, y: 0.99))
|
||||
bezierPath.close()
|
||||
bezierPath.move(to: CGPoint(x: 19.9, y: 4.39))
|
||||
bezierPath.addCurve(to: CGPoint(x: 27.52, y: 4.5), controlPoint1: CGPoint(x: 24.94, y: 4.39), controlPoint2: CGPoint(x: 25.53, y: 4.41))
|
||||
bezierPath.addCurve(to: CGPoint(x: 31.03, y: 5.15), controlPoint1: CGPoint(x: 29.36, y: 4.58), controlPoint2: CGPoint(x: 30.36, y: 4.89))
|
||||
bezierPath.addCurve(to: CGPoint(x: 33.2, y: 6.56), controlPoint1: CGPoint(x: 31.91, y: 5.49), controlPoint2: CGPoint(x: 32.54, y: 5.9))
|
||||
bezierPath.addCurve(to: CGPoint(x: 34.61, y: 8.73), controlPoint1: CGPoint(x: 33.86, y: 7.22), controlPoint2: CGPoint(x: 34.27, y: 7.85))
|
||||
bezierPath.addCurve(to: CGPoint(x: 35.26, y: 12.23), controlPoint1: CGPoint(x: 34.87, y: 9.39), controlPoint2: CGPoint(x: 35.18, y: 10.39))
|
||||
bezierPath.addCurve(to: CGPoint(x: 35.37, y: 19.86), controlPoint1: CGPoint(x: 35.35, y: 14.22), controlPoint2: CGPoint(x: 35.37, y: 14.82))
|
||||
bezierPath.addCurve(to: CGPoint(x: 35.26, y: 27.48), controlPoint1: CGPoint(x: 35.37, y: 24.9), controlPoint2: CGPoint(x: 35.35, y: 25.49))
|
||||
bezierPath.addCurve(to: CGPoint(x: 34.61, y: 30.99), controlPoint1: CGPoint(x: 35.18, y: 29.32), controlPoint2: CGPoint(x: 34.87, y: 30.32))
|
||||
bezierPath.addCurve(to: CGPoint(x: 33.2, y: 33.16), controlPoint1: CGPoint(x: 34.27, y: 31.87), controlPoint2: CGPoint(x: 33.86, y: 32.5))
|
||||
bezierPath.addCurve(to: CGPoint(x: 31.03, y: 34.57), controlPoint1: CGPoint(x: 32.54, y: 33.82), controlPoint2: CGPoint(x: 31.91, y: 34.23))
|
||||
bezierPath.addCurve(to: CGPoint(x: 27.52, y: 35.22), controlPoint1: CGPoint(x: 30.36, y: 34.83), controlPoint2: CGPoint(x: 29.36, y: 35.14))
|
||||
bezierPath.addCurve(to: CGPoint(x: 19.9, y: 35.33), controlPoint1: CGPoint(x: 25.53, y: 35.31), controlPoint2: CGPoint(x: 24.94, y: 35.33))
|
||||
bezierPath.addCurve(to: CGPoint(x: 12.27, y: 35.22), controlPoint1: CGPoint(x: 14.86, y: 35.33), controlPoint2: CGPoint(x: 14.26, y: 35.31))
|
||||
bezierPath.addCurve(to: CGPoint(x: 8.77, y: 34.57), controlPoint1: CGPoint(x: 10.43, y: 35.14), controlPoint2: CGPoint(x: 9.43, y: 34.83))
|
||||
bezierPath.addCurve(to: CGPoint(x: 6.6, y: 33.16), controlPoint1: CGPoint(x: 7.89, y: 34.23), controlPoint2: CGPoint(x: 7.26, y: 33.82))
|
||||
bezierPath.addCurve(to: CGPoint(x: 5.19, y: 30.99), controlPoint1: CGPoint(x: 5.94, y: 32.5), controlPoint2: CGPoint(x: 5.53, y: 31.87))
|
||||
bezierPath.addCurve(to: CGPoint(x: 4.54, y: 27.48), controlPoint1: CGPoint(x: 4.93, y: 30.32), controlPoint2: CGPoint(x: 4.62, y: 29.32))
|
||||
bezierPath.addCurve(to: CGPoint(x: 4.43, y: 19.86), controlPoint1: CGPoint(x: 4.45, y: 25.49), controlPoint2: CGPoint(x: 4.43, y: 24.9))
|
||||
bezierPath.addCurve(to: CGPoint(x: 4.54, y: 12.23), controlPoint1: CGPoint(x: 4.43, y: 14.82), controlPoint2: CGPoint(x: 4.45, y: 14.22))
|
||||
bezierPath.addCurve(to: CGPoint(x: 5.19, y: 8.73), controlPoint1: CGPoint(x: 4.62, y: 10.39), controlPoint2: CGPoint(x: 4.93, y: 9.39))
|
||||
bezierPath.addCurve(to: CGPoint(x: 6.6, y: 6.56), controlPoint1: CGPoint(x: 5.53, y: 7.85), controlPoint2: CGPoint(x: 5.94, y: 7.22))
|
||||
bezierPath.addCurve(to: CGPoint(x: 8.77, y: 5.15), controlPoint1: CGPoint(x: 7.26, y: 5.9), controlPoint2: CGPoint(x: 7.89, y: 5.49))
|
||||
bezierPath.addCurve(to: CGPoint(x: 12.27, y: 4.5), controlPoint1: CGPoint(x: 9.43, y: 4.89), controlPoint2: CGPoint(x: 10.43, y: 4.58))
|
||||
bezierPath.addCurve(to: CGPoint(x: 19.9, y: 4.39), controlPoint1: CGPoint(x: 14.26, y: 4.41), controlPoint2: CGPoint(x: 14.86, y: 4.39))
|
||||
bezierPath.addLine(to: CGPoint(x: 19.9, y: 4.39))
|
||||
bezierPath.close()
|
||||
bezierPath.move(to: CGPoint(x: 19.9, y: 26.15))
|
||||
bezierPath.addCurve(to: CGPoint(x: 13.61, y: 19.86), controlPoint1: CGPoint(x: 16.42, y: 26.15), controlPoint2: CGPoint(x: 13.61, y: 23.33))
|
||||
bezierPath.addCurve(to: CGPoint(x: 19.9, y: 13.57), controlPoint1: CGPoint(x: 13.61, y: 16.38), controlPoint2: CGPoint(x: 16.42, y: 13.57))
|
||||
bezierPath.addCurve(to: CGPoint(x: 26.19, y: 19.86), controlPoint1: CGPoint(x: 23.37, y: 13.57), controlPoint2: CGPoint(x: 26.19, y: 16.38))
|
||||
bezierPath.addCurve(to: CGPoint(x: 19.9, y: 26.15), controlPoint1: CGPoint(x: 26.19, y: 23.33), controlPoint2: CGPoint(x: 23.37, y: 26.15))
|
||||
bezierPath.close()
|
||||
bezierPath.move(to: CGPoint(x: 19.9, y: 10.17))
|
||||
bezierPath.addCurve(to: CGPoint(x: 10.21, y: 19.86), controlPoint1: CGPoint(x: 14.55, y: 10.17), controlPoint2: CGPoint(x: 10.21, y: 14.51))
|
||||
bezierPath.addCurve(to: CGPoint(x: 19.9, y: 29.55), controlPoint1: CGPoint(x: 10.21, y: 25.21), controlPoint2: CGPoint(x: 14.55, y: 29.55))
|
||||
bezierPath.addCurve(to: CGPoint(x: 29.59, y: 19.86), controlPoint1: CGPoint(x: 25.25, y: 29.55), controlPoint2: CGPoint(x: 29.59, y: 25.21))
|
||||
bezierPath.addCurve(to: CGPoint(x: 19.9, y: 10.17), controlPoint1: CGPoint(x: 29.59, y: 14.51), controlPoint2: CGPoint(x: 25.25, y: 10.17))
|
||||
bezierPath.addLine(to: CGPoint(x: 19.9, y: 10.17))
|
||||
bezierPath.close()
|
||||
bezierPath.move(to: CGPoint(x: 32.43, y: 9.57))
|
||||
bezierPath.addCurve(to: CGPoint(x: 30.23, y: 11.77), controlPoint1: CGPoint(x: 32.43, y: 10.79), controlPoint2: CGPoint(x: 31.44, y: 11.77))
|
||||
bezierPath.addCurve(to: CGPoint(x: 28.03, y: 9.57), controlPoint1: CGPoint(x: 29.01, y: 11.77), controlPoint2: CGPoint(x: 28.03, y: 10.79))
|
||||
bezierPath.addCurve(to: CGPoint(x: 30.23, y: 7.38), controlPoint1: CGPoint(x: 28.03, y: 8.36), controlPoint2: CGPoint(x: 29.01, y: 7.38))
|
||||
bezierPath.addCurve(to: CGPoint(x: 32.43, y: 9.57), controlPoint1: CGPoint(x: 31.44, y: 7.38), controlPoint2: CGPoint(x: 32.43, y: 8.36))
|
||||
bezierPath.close()
|
||||
fillColor.setFill()
|
||||
bezierPath.fill()
|
||||
|
||||
context.restoreGState()
|
||||
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawVK(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
//// Resize to Target Frame
|
||||
context.saveGState()
|
||||
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 40, height: 40), target: targetFrame)
|
||||
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
|
||||
context.scaleBy(x: resizedFrame.width / 40, y: resizedFrame.height / 40)
|
||||
|
||||
|
||||
//// Bezier Drawing
|
||||
let bezierPath = UIBezierPath()
|
||||
bezierPath.move(to: CGPoint(x: 32.57, y: 19.79))
|
||||
bezierPath.addCurve(to: CGPoint(x: 38.07, y: 10.51), controlPoint1: CGPoint(x: 32.57, y: 19.79), controlPoint2: CGPoint(x: 37.57, y: 12.8))
|
||||
bezierPath.addCurve(to: CGPoint(x: 37.01, y: 9.23), controlPoint1: CGPoint(x: 38.24, y: 9.69), controlPoint2: CGPoint(x: 37.87, y: 9.23))
|
||||
bezierPath.addCurve(to: CGPoint(x: 32.67, y: 9.23), controlPoint1: CGPoint(x: 37.01, y: 9.23), controlPoint2: CGPoint(x: 34.13, y: 9.23))
|
||||
bezierPath.addCurve(to: CGPoint(x: 31.01, y: 10.28), controlPoint1: CGPoint(x: 31.67, y: 9.23), controlPoint2: CGPoint(x: 31.31, y: 9.66))
|
||||
bezierPath.addCurve(to: CGPoint(x: 25.8, y: 18.34), controlPoint1: CGPoint(x: 31.01, y: 10.28), controlPoint2: CGPoint(x: 28.66, y: 15.23))
|
||||
bezierPath.addCurve(to: CGPoint(x: 23.91, y: 19.66), controlPoint1: CGPoint(x: 24.89, y: 19.35), controlPoint2: CGPoint(x: 24.42, y: 19.66))
|
||||
bezierPath.addCurve(to: CGPoint(x: 23.32, y: 18.41), controlPoint1: CGPoint(x: 23.5, y: 19.66), controlPoint2: CGPoint(x: 23.32, y: 19.32))
|
||||
bezierPath.addLine(to: CGPoint(x: 23.32, y: 10.44))
|
||||
bezierPath.addCurve(to: CGPoint(x: 22.25, y: 9), controlPoint1: CGPoint(x: 23.32, y: 9.33), controlPoint2: CGPoint(x: 23.18, y: 9))
|
||||
bezierPath.addLine(to: CGPoint(x: 15.29, y: 9))
|
||||
bezierPath.addCurve(to: CGPoint(x: 14.43, y: 9.72), controlPoint1: CGPoint(x: 14.76, y: 9), controlPoint2: CGPoint(x: 14.43, y: 9.31))
|
||||
bezierPath.addCurve(to: CGPoint(x: 16.05, y: 13.85), controlPoint1: CGPoint(x: 14.43, y: 10.77), controlPoint2: CGPoint(x: 16.05, y: 11.01))
|
||||
bezierPath.addLine(to: CGPoint(x: 16.05, y: 19.72))
|
||||
bezierPath.addCurve(to: CGPoint(x: 15.42, y: 21.36), controlPoint1: CGPoint(x: 16.05, y: 20.9), controlPoint2: CGPoint(x: 15.99, y: 21.36))
|
||||
bezierPath.addCurve(to: CGPoint(x: 8.39, y: 10.51), controlPoint1: CGPoint(x: 13.93, y: 21.36), controlPoint2: CGPoint(x: 10.38, y: 16.28))
|
||||
bezierPath.addCurve(to: CGPoint(x: 6.44, y: 9), controlPoint1: CGPoint(x: 8, y: 9.33), controlPoint2: CGPoint(x: 7.57, y: 9))
|
||||
bezierPath.addLine(to: CGPoint(x: 2.09, y: 9))
|
||||
bezierPath.addCurve(to: CGPoint(x: 1, y: 10.05), controlPoint1: CGPoint(x: 1.46, y: 9), controlPoint2: CGPoint(x: 1, y: 9.43))
|
||||
bezierPath.addCurve(to: CGPoint(x: 7.7, y: 23.62), controlPoint1: CGPoint(x: 1, y: 11.2), controlPoint2: CGPoint(x: 2.36, y: 16.51))
|
||||
bezierPath.addCurve(to: CGPoint(x: 20.23, y: 31), controlPoint1: CGPoint(x: 11.28, y: 28.41), controlPoint2: CGPoint(x: 15.99, y: 31))
|
||||
bezierPath.addCurve(to: CGPoint(x: 23.45, y: 29.49), controlPoint1: CGPoint(x: 22.82, y: 31), controlPoint2: CGPoint(x: 23.45, y: 30.57))
|
||||
bezierPath.addLine(to: CGPoint(x: 23.45, y: 25.82))
|
||||
bezierPath.addCurve(to: CGPoint(x: 24.34, y: 24.51), controlPoint1: CGPoint(x: 23.45, y: 24.9), controlPoint2: CGPoint(x: 23.81, y: 24.51))
|
||||
bezierPath.addCurve(to: CGPoint(x: 28.46, y: 27.1), controlPoint1: CGPoint(x: 24.94, y: 24.51), controlPoint2: CGPoint(x: 25.99, y: 24.7))
|
||||
bezierPath.addCurve(to: CGPoint(x: 33.16, y: 31), controlPoint1: CGPoint(x: 31.37, y: 29.85), controlPoint2: CGPoint(x: 31.57, y: 31))
|
||||
bezierPath.addLine(to: CGPoint(x: 38.04, y: 31))
|
||||
bezierPath.addCurve(to: CGPoint(x: 39, y: 29.95), controlPoint1: CGPoint(x: 38.54, y: 31), controlPoint2: CGPoint(x: 39, y: 30.77))
|
||||
bezierPath.addCurve(to: CGPoint(x: 35.39, y: 24.64), controlPoint1: CGPoint(x: 39, y: 28.87), controlPoint2: CGPoint(x: 37.57, y: 26.93))
|
||||
bezierPath.addCurve(to: CGPoint(x: 32.57, y: 21.59), controlPoint1: CGPoint(x: 34.49, y: 23.46), controlPoint2: CGPoint(x: 33.03, y: 22.18))
|
||||
bezierPath.addCurve(to: CGPoint(x: 32.57, y: 19.79), controlPoint1: CGPoint(x: 31.9, y: 20.9), controlPoint2: CGPoint(x: 32.1, y: 20.51))
|
||||
bezierPath.close()
|
||||
bezierPath.usesEvenOddFillRule = true
|
||||
fillColor.setFill()
|
||||
bezierPath.fill()
|
||||
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawWhatsapp(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
//// Resize to Target Frame
|
||||
context.saveGState()
|
||||
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 40, height: 40), target: targetFrame)
|
||||
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
|
||||
context.scaleBy(x: resizedFrame.width / 40, y: resizedFrame.height / 40)
|
||||
|
||||
|
||||
//// Bezier Drawing
|
||||
let bezierPath = UIBezierPath()
|
||||
bezierPath.move(to: CGPoint(x: 28.52, y: 22.97))
|
||||
bezierPath.addCurve(to: CGPoint(x: 25.54, y: 21.56), controlPoint1: CGPoint(x: 28.08, y: 22.76), controlPoint2: CGPoint(x: 25.94, y: 21.71))
|
||||
bezierPath.addCurve(to: CGPoint(x: 24.56, y: 21.78), controlPoint1: CGPoint(x: 25.14, y: 21.42), controlPoint2: CGPoint(x: 24.85, y: 21.35))
|
||||
bezierPath.addCurve(to: CGPoint(x: 23.18, y: 23.48), controlPoint1: CGPoint(x: 24.27, y: 22.21), controlPoint2: CGPoint(x: 23.44, y: 23.19))
|
||||
bezierPath.addCurve(to: CGPoint(x: 22.24, y: 23.59), controlPoint1: CGPoint(x: 22.93, y: 23.77), controlPoint2: CGPoint(x: 22.68, y: 23.81))
|
||||
bezierPath.addCurve(to: CGPoint(x: 18.74, y: 21.44), controlPoint1: CGPoint(x: 21.81, y: 23.37), controlPoint2: CGPoint(x: 20.4, y: 22.91))
|
||||
bezierPath.addCurve(to: CGPoint(x: 16.32, y: 18.44), controlPoint1: CGPoint(x: 17.44, y: 20.29), controlPoint2: CGPoint(x: 16.57, y: 18.87))
|
||||
bezierPath.addCurve(to: CGPoint(x: 16.51, y: 17.55), controlPoint1: CGPoint(x: 16.06, y: 18), controlPoint2: CGPoint(x: 16.29, y: 17.77))
|
||||
bezierPath.addCurve(to: CGPoint(x: 17.16, y: 16.79), controlPoint1: CGPoint(x: 16.7, y: 17.36), controlPoint2: CGPoint(x: 16.94, y: 17.05))
|
||||
bezierPath.addCurve(to: CGPoint(x: 17.6, y: 16.07), controlPoint1: CGPoint(x: 17.38, y: 16.54), controlPoint2: CGPoint(x: 17.45, y: 16.36))
|
||||
bezierPath.addCurve(to: CGPoint(x: 17.56, y: 15.31), controlPoint1: CGPoint(x: 17.74, y: 15.78), controlPoint2: CGPoint(x: 17.67, y: 15.53))
|
||||
bezierPath.addCurve(to: CGPoint(x: 16.22, y: 12.09), controlPoint1: CGPoint(x: 17.45, y: 15.09), controlPoint2: CGPoint(x: 16.58, y: 12.96))
|
||||
bezierPath.addCurve(to: CGPoint(x: 15.24, y: 11.35), controlPoint1: CGPoint(x: 15.86, y: 11.25), controlPoint2: CGPoint(x: 15.51, y: 11.36))
|
||||
bezierPath.addCurve(to: CGPoint(x: 14.4, y: 11.33), controlPoint1: CGPoint(x: 14.98, y: 11.34), controlPoint2: CGPoint(x: 14.69, y: 11.33))
|
||||
bezierPath.addCurve(to: CGPoint(x: 13.24, y: 11.88), controlPoint1: CGPoint(x: 14.11, y: 11.33), controlPoint2: CGPoint(x: 13.64, y: 11.44))
|
||||
bezierPath.addCurve(to: CGPoint(x: 11.72, y: 15.49), controlPoint1: CGPoint(x: 12.84, y: 12.31), controlPoint2: CGPoint(x: 11.72, y: 13.36))
|
||||
bezierPath.addCurve(to: CGPoint(x: 13.5, y: 19.97), controlPoint1: CGPoint(x: 11.72, y: 17.62), controlPoint2: CGPoint(x: 13.28, y: 19.68))
|
||||
bezierPath.addCurve(to: CGPoint(x: 20.93, y: 26.52), controlPoint1: CGPoint(x: 13.71, y: 20.26), controlPoint2: CGPoint(x: 16.57, y: 24.64))
|
||||
bezierPath.addCurve(to: CGPoint(x: 23.42, y: 27.43), controlPoint1: CGPoint(x: 21.97, y: 26.96), controlPoint2: CGPoint(x: 22.78, y: 27.23))
|
||||
bezierPath.addCurve(to: CGPoint(x: 26.16, y: 27.6), controlPoint1: CGPoint(x: 24.46, y: 27.76), controlPoint2: CGPoint(x: 25.41, y: 27.71))
|
||||
bezierPath.addCurve(to: CGPoint(x: 29.1, y: 25.54), controlPoint1: CGPoint(x: 27, y: 27.48), controlPoint2: CGPoint(x: 28.74, y: 26.55))
|
||||
bezierPath.addCurve(to: CGPoint(x: 29.35, y: 23.48), controlPoint1: CGPoint(x: 29.46, y: 24.53), controlPoint2: CGPoint(x: 29.46, y: 23.66))
|
||||
bezierPath.addCurve(to: CGPoint(x: 28.52, y: 22.97), controlPoint1: CGPoint(x: 29.24, y: 23.3), controlPoint2: CGPoint(x: 28.95, y: 23.19))
|
||||
bezierPath.close()
|
||||
bezierPath.move(to: CGPoint(x: 20.57, y: 33.77))
|
||||
bezierPath.addLine(to: CGPoint(x: 20.57, y: 33.77))
|
||||
bezierPath.addCurve(to: CGPoint(x: 13.2, y: 31.76), controlPoint1: CGPoint(x: 17.97, y: 33.77), controlPoint2: CGPoint(x: 15.42, y: 33.07))
|
||||
bezierPath.addLine(to: CGPoint(x: 12.67, y: 31.45))
|
||||
bezierPath.addLine(to: CGPoint(x: 7.18, y: 32.88))
|
||||
bezierPath.addLine(to: CGPoint(x: 8.65, y: 27.56))
|
||||
bezierPath.addLine(to: CGPoint(x: 8.3, y: 27.01))
|
||||
bezierPath.addCurve(to: CGPoint(x: 6.09, y: 19.34), controlPoint1: CGPoint(x: 6.85, y: 24.72), controlPoint2: CGPoint(x: 6.09, y: 22.07))
|
||||
bezierPath.addCurve(to: CGPoint(x: 20.58, y: 4.93), controlPoint1: CGPoint(x: 6.09, y: 11.4), controlPoint2: CGPoint(x: 12.59, y: 4.93))
|
||||
bezierPath.addCurve(to: CGPoint(x: 30.82, y: 9.16), controlPoint1: CGPoint(x: 24.45, y: 4.93), controlPoint2: CGPoint(x: 28.08, y: 6.43))
|
||||
bezierPath.addCurve(to: CGPoint(x: 35.06, y: 19.35), controlPoint1: CGPoint(x: 33.55, y: 11.88), controlPoint2: CGPoint(x: 35.06, y: 15.5))
|
||||
bezierPath.addCurve(to: CGPoint(x: 20.57, y: 33.77), controlPoint1: CGPoint(x: 35.05, y: 27.3), controlPoint2: CGPoint(x: 28.56, y: 33.77))
|
||||
bezierPath.close()
|
||||
bezierPath.move(to: CGPoint(x: 32.9, y: 7.09))
|
||||
bezierPath.addCurve(to: CGPoint(x: 20.57, y: 2), controlPoint1: CGPoint(x: 29.61, y: 3.81), controlPoint2: CGPoint(x: 25.23, y: 2))
|
||||
bezierPath.addCurve(to: CGPoint(x: 3.15, y: 19.34), controlPoint1: CGPoint(x: 10.97, y: 2), controlPoint2: CGPoint(x: 3.15, y: 9.78))
|
||||
bezierPath.addCurve(to: CGPoint(x: 5.47, y: 28.01), controlPoint1: CGPoint(x: 3.14, y: 22.4), controlPoint2: CGPoint(x: 3.95, y: 25.38))
|
||||
bezierPath.addLine(to: CGPoint(x: 3, y: 37))
|
||||
bezierPath.addLine(to: CGPoint(x: 12.24, y: 34.59))
|
||||
bezierPath.addCurve(to: CGPoint(x: 20.57, y: 36.7), controlPoint1: CGPoint(x: 14.78, y: 35.97), controlPoint2: CGPoint(x: 17.65, y: 36.7))
|
||||
bezierPath.addLine(to: CGPoint(x: 20.57, y: 36.7))
|
||||
bezierPath.addLine(to: CGPoint(x: 20.57, y: 36.7))
|
||||
bezierPath.addCurve(to: CGPoint(x: 38, y: 19.36), controlPoint1: CGPoint(x: 30.18, y: 36.7), controlPoint2: CGPoint(x: 38, y: 28.92))
|
||||
bezierPath.addCurve(to: CGPoint(x: 32.9, y: 7.09), controlPoint1: CGPoint(x: 38, y: 14.72), controlPoint2: CGPoint(x: 36.19, y: 10.36))
|
||||
bezierPath.close()
|
||||
bezierPath.usesEvenOddFillRule = true
|
||||
fillColor.setFill()
|
||||
bezierPath.fill()
|
||||
|
||||
context.restoreGState()
|
||||
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawTelegram(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
//// Resize to Target Frame
|
||||
context.saveGState()
|
||||
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 40, height: 40), target: targetFrame)
|
||||
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
|
||||
context.scaleBy(x: resizedFrame.width / 40, y: resizedFrame.height / 40)
|
||||
|
||||
|
||||
//// Bezier Drawing
|
||||
let bezierPath = UIBezierPath()
|
||||
bezierPath.move(to: CGPoint(x: 35.15, y: 4.5))
|
||||
bezierPath.addCurve(to: CGPoint(x: 33.94, y: 4.79), controlPoint1: CGPoint(x: 34.74, y: 4.51), controlPoint2: CGPoint(x: 34.32, y: 4.62))
|
||||
bezierPath.addCurve(to: CGPoint(x: 18.74, y: 10.7), controlPoint1: CGPoint(x: 33.25, y: 5.06), controlPoint2: CGPoint(x: 26, y: 7.88))
|
||||
bezierPath.addCurve(to: CGPoint(x: 8.69, y: 14.6), controlPoint1: CGPoint(x: 15.11, y: 12.12), controlPoint2: CGPoint(x: 11.49, y: 13.52))
|
||||
bezierPath.addCurve(to: CGPoint(x: 3.67, y: 16.54), controlPoint1: CGPoint(x: 5.9, y: 15.69), controlPoint2: CGPoint(x: 3.87, y: 16.47))
|
||||
bezierPath.addCurve(to: CGPoint(x: 1.4, y: 17.91), controlPoint1: CGPoint(x: 3.01, y: 16.77), controlPoint2: CGPoint(x: 2.05, y: 17.15))
|
||||
bezierPath.addCurve(to: CGPoint(x: 1.12, y: 19.55), controlPoint1: CGPoint(x: 1.08, y: 18.28), controlPoint2: CGPoint(x: 0.84, y: 18.97))
|
||||
bezierPath.addCurve(to: CGPoint(x: 2.72, y: 20.75), controlPoint1: CGPoint(x: 1.41, y: 20.13), controlPoint2: CGPoint(x: 1.95, y: 20.46))
|
||||
bezierPath.addLine(to: CGPoint(x: 2.74, y: 20.76))
|
||||
bezierPath.addLine(to: CGPoint(x: 2.76, y: 20.77))
|
||||
bezierPath.addCurve(to: CGPoint(x: 10.35, y: 23.15), controlPoint1: CGPoint(x: 5.56, y: 21.64), controlPoint2: CGPoint(x: 9.78, y: 22.97))
|
||||
bezierPath.addCurve(to: CGPoint(x: 13.25, y: 32.72), controlPoint1: CGPoint(x: 10.5, y: 23.63), controlPoint2: CGPoint(x: 12.28, y: 29.53))
|
||||
bezierPath.addCurve(to: CGPoint(x: 14.93, y: 33.73), controlPoint1: CGPoint(x: 13.5, y: 33.38), controlPoint2: CGPoint(x: 14.24, y: 33.82))
|
||||
bezierPath.addCurve(to: CGPoint(x: 15.53, y: 33.67), controlPoint1: CGPoint(x: 15.05, y: 33.73), controlPoint2: CGPoint(x: 15.28, y: 33.73))
|
||||
bezierPath.addCurve(to: CGPoint(x: 16.83, y: 32.94), controlPoint1: CGPoint(x: 15.9, y: 33.58), controlPoint2: CGPoint(x: 16.38, y: 33.37))
|
||||
bezierPath.addLine(to: CGPoint(x: 16.83, y: 32.94))
|
||||
bezierPath.addCurve(to: CGPoint(x: 20.46, y: 29.4), controlPoint1: CGPoint(x: 17.48, y: 32.33), controlPoint2: CGPoint(x: 19.78, y: 30.07))
|
||||
bezierPath.addLine(to: CGPoint(x: 27.96, y: 34.96))
|
||||
bezierPath.addLine(to: CGPoint(x: 27.99, y: 34.98))
|
||||
bezierPath.addCurve(to: CGPoint(x: 29.57, y: 35.49), controlPoint1: CGPoint(x: 27.99, y: 34.98), controlPoint2: CGPoint(x: 28.67, y: 35.43))
|
||||
bezierPath.addCurve(to: CGPoint(x: 31.04, y: 35.09), controlPoint1: CGPoint(x: 30.01, y: 35.52), controlPoint2: CGPoint(x: 30.56, y: 35.44))
|
||||
bezierPath.addCurve(to: CGPoint(x: 32.02, y: 33.42), controlPoint1: CGPoint(x: 31.51, y: 34.75), controlPoint2: CGPoint(x: 31.85, y: 34.17))
|
||||
bezierPath.addCurve(to: CGPoint(x: 37.28, y: 8.61), controlPoint1: CGPoint(x: 32.61, y: 30.86), controlPoint2: CGPoint(x: 36.6, y: 11.79))
|
||||
bezierPath.addCurve(to: CGPoint(x: 36.71, y: 5.03), controlPoint1: CGPoint(x: 37.71, y: 6.99), controlPoint2: CGPoint(x: 37.51, y: 5.73))
|
||||
bezierPath.addCurve(to: CGPoint(x: 35.33, y: 4.5), controlPoint1: CGPoint(x: 36.3, y: 4.68), controlPoint2: CGPoint(x: 35.82, y: 4.52))
|
||||
bezierPath.addCurve(to: CGPoint(x: 35.15, y: 4.5), controlPoint1: CGPoint(x: 35.27, y: 4.5), controlPoint2: CGPoint(x: 35.21, y: 4.5))
|
||||
bezierPath.addLine(to: CGPoint(x: 35.15, y: 4.5))
|
||||
bezierPath.close()
|
||||
bezierPath.move(to: CGPoint(x: 35.24, y: 6.07))
|
||||
bezierPath.addCurve(to: CGPoint(x: 35.68, y: 6.21), controlPoint1: CGPoint(x: 35.43, y: 6.07), controlPoint2: CGPoint(x: 35.59, y: 6.12))
|
||||
bezierPath.addCurve(to: CGPoint(x: 35.77, y: 8.23), controlPoint1: CGPoint(x: 35.88, y: 6.38), controlPoint2: CGPoint(x: 36.13, y: 6.89))
|
||||
bezierPath.addCurve(to: CGPoint(x: 30.51, y: 33.07), controlPoint1: CGPoint(x: 33.84, y: 16.86), controlPoint2: CGPoint(x: 32.16, y: 25.68))
|
||||
bezierPath.addCurve(to: CGPoint(x: 30.12, y: 33.83), controlPoint1: CGPoint(x: 30.4, y: 33.57), controlPoint2: CGPoint(x: 30.24, y: 33.75))
|
||||
bezierPath.addCurve(to: CGPoint(x: 29.67, y: 33.94), controlPoint1: CGPoint(x: 30, y: 33.92), controlPoint2: CGPoint(x: 29.87, y: 33.95))
|
||||
bezierPath.addCurve(to: CGPoint(x: 28.82, y: 33.66), controlPoint1: CGPoint(x: 29.28, y: 33.91), controlPoint2: CGPoint(x: 28.84, y: 33.67))
|
||||
bezierPath.addLine(to: CGPoint(x: 16.66, y: 24.63))
|
||||
bezierPath.addCurve(to: CGPoint(x: 30.24, y: 11.88), controlPoint1: CGPoint(x: 17.86, y: 23.5), controlPoint2: CGPoint(x: 25.86, y: 15.9))
|
||||
bezierPath.addCurve(to: CGPoint(x: 29.69, y: 10.6), controlPoint1: CGPoint(x: 30.72, y: 11.42), controlPoint2: CGPoint(x: 30.33, y: 10.59))
|
||||
bezierPath.addCurve(to: CGPoint(x: 27.45, y: 11.72), controlPoint1: CGPoint(x: 28.87, y: 10.76), controlPoint2: CGPoint(x: 28.19, y: 11.35))
|
||||
bezierPath.addCurve(to: CGPoint(x: 10.91, y: 21.68), controlPoint1: CGPoint(x: 22.04, y: 14.9), controlPoint2: CGPoint(x: 11.81, y: 21.13))
|
||||
bezierPath.addCurve(to: CGPoint(x: 3.26, y: 19.29), controlPoint1: CGPoint(x: 10.44, y: 21.53), controlPoint2: CGPoint(x: 6.11, y: 20.17))
|
||||
bezierPath.addCurve(to: CGPoint(x: 2.59, y: 18.92), controlPoint1: CGPoint(x: 2.81, y: 19.11), controlPoint2: CGPoint(x: 2.65, y: 18.97))
|
||||
bezierPath.addCurve(to: CGPoint(x: 2.59, y: 18.92), controlPoint1: CGPoint(x: 2.59, y: 18.92), controlPoint2: CGPoint(x: 2.59, y: 18.92))
|
||||
bezierPath.addCurve(to: CGPoint(x: 4.19, y: 18.01), controlPoint1: CGPoint(x: 2.8, y: 18.67), controlPoint2: CGPoint(x: 3.67, y: 18.19))
|
||||
bezierPath.addCurve(to: CGPoint(x: 9.25, y: 16.06), controlPoint1: CGPoint(x: 4.57, y: 17.87), controlPoint2: CGPoint(x: 6.46, y: 17.14))
|
||||
bezierPath.addCurve(to: CGPoint(x: 19.3, y: 12.16), controlPoint1: CGPoint(x: 12.05, y: 14.98), controlPoint2: CGPoint(x: 15.67, y: 13.57))
|
||||
bezierPath.addCurve(to: CGPoint(x: 34.56, y: 6.23), controlPoint1: CGPoint(x: 24.39, y: 10.19), controlPoint2: CGPoint(x: 29.48, y: 8.21))
|
||||
bezierPath.addCurve(to: CGPoint(x: 35.24, y: 6.07), controlPoint1: CGPoint(x: 34.81, y: 6.11), controlPoint2: CGPoint(x: 35.04, y: 6.06))
|
||||
bezierPath.addLine(to: CGPoint(x: 35.24, y: 6.07))
|
||||
bezierPath.close()
|
||||
bezierPath.move(to: CGPoint(x: 16.15, y: 26.2))
|
||||
bezierPath.addLine(to: CGPoint(x: 19.19, y: 28.45))
|
||||
bezierPath.addCurve(to: CGPoint(x: 15.79, y: 31.79), controlPoint1: CGPoint(x: 18.35, y: 29.29), controlPoint2: CGPoint(x: 16.35, y: 31.26))
|
||||
bezierPath.addLine(to: CGPoint(x: 16.15, y: 26.2))
|
||||
bezierPath.addLine(to: CGPoint(x: 16.15, y: 26.2))
|
||||
bezierPath.close()
|
||||
fillColor.setFill()
|
||||
bezierPath.fill()
|
||||
|
||||
context.restoreGState()
|
||||
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawFacebook(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 41, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
//// Resize to Target Frame
|
||||
context.saveGState()
|
||||
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 41, height: 40), target: targetFrame)
|
||||
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
|
||||
context.scaleBy(x: resizedFrame.width / 41, y: resizedFrame.height / 40)
|
||||
|
||||
|
||||
//// Bezier Drawing
|
||||
let bezierPath = UIBezierPath()
|
||||
bezierPath.move(to: CGPoint(x: 22.36, y: 21.49))
|
||||
bezierPath.addLine(to: CGPoint(x: 27.52, y: 21.49))
|
||||
bezierPath.addLine(to: CGPoint(x: 28.29, y: 15.45))
|
||||
bezierPath.addLine(to: CGPoint(x: 22.36, y: 15.45))
|
||||
bezierPath.addLine(to: CGPoint(x: 22.36, y: 11.59))
|
||||
bezierPath.addCurve(to: CGPoint(x: 25.33, y: 8.65), controlPoint1: CGPoint(x: 22.36, y: 9.84), controlPoint2: CGPoint(x: 22.84, y: 8.65))
|
||||
bezierPath.addLine(to: CGPoint(x: 28.5, y: 8.64))
|
||||
bezierPath.addLine(to: CGPoint(x: 28.5, y: 3.24))
|
||||
bezierPath.addCurve(to: CGPoint(x: 23.88, y: 3), controlPoint1: CGPoint(x: 27.95, y: 3.16), controlPoint2: CGPoint(x: 26.07, y: 3))
|
||||
bezierPath.addCurve(to: CGPoint(x: 16.17, y: 10.99), controlPoint1: CGPoint(x: 19.3, y: 3), controlPoint2: CGPoint(x: 16.17, y: 5.82))
|
||||
bezierPath.addLine(to: CGPoint(x: 16.17, y: 15.45))
|
||||
bezierPath.addLine(to: CGPoint(x: 11, y: 15.45))
|
||||
bezierPath.addLine(to: CGPoint(x: 11, y: 21.49))
|
||||
bezierPath.addLine(to: CGPoint(x: 16.17, y: 21.49))
|
||||
bezierPath.addLine(to: CGPoint(x: 16.17, y: 37))
|
||||
bezierPath.addLine(to: CGPoint(x: 22.36, y: 37))
|
||||
bezierPath.addLine(to: CGPoint(x: 22.36, y: 21.49))
|
||||
bezierPath.close()
|
||||
bezierPath.usesEvenOddFillRule = true
|
||||
fillColor.setFill()
|
||||
bezierPath.fill()
|
||||
|
||||
context.restoreGState()
|
||||
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawViber(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 41, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
//// Resize to Target Frame
|
||||
context.saveGState()
|
||||
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 41, height: 40), target: targetFrame)
|
||||
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
|
||||
context.scaleBy(x: resizedFrame.width / 41, y: resizedFrame.height / 40)
|
||||
|
||||
|
||||
//// Bezier 5 Drawing
|
||||
let bezier5Path = UIBezierPath()
|
||||
bezier5Path.move(to: CGPoint(x: 34, y: 16.95))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 23.83, y: 6), controlPoint1: CGPoint(x: 34.05, y: 11.59), controlPoint2: CGPoint(x: 29.49, y: 6.68))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 23.47, y: 5.94), controlPoint1: CGPoint(x: 23.72, y: 5.98), controlPoint2: CGPoint(x: 23.6, y: 5.96))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 22.61, y: 5.85), controlPoint1: CGPoint(x: 23.19, y: 5.9), controlPoint2: CGPoint(x: 22.9, y: 5.85))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 21.06, y: 7.15), controlPoint1: CGPoint(x: 21.45, y: 5.85), controlPoint2: CGPoint(x: 21.14, y: 6.66))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 21.28, y: 8.33), controlPoint1: CGPoint(x: 20.98, y: 7.62), controlPoint2: CGPoint(x: 21.05, y: 8.02))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 22.88, y: 9.02), controlPoint1: CGPoint(x: 21.67, y: 8.85), controlPoint2: CGPoint(x: 22.34, y: 8.94))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 23.32, y: 9.09), controlPoint1: CGPoint(x: 23.04, y: 9.04), controlPoint2: CGPoint(x: 23.19, y: 9.06))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 30.94, y: 17.04), controlPoint1: CGPoint(x: 28.4, y: 10.22), controlPoint2: CGPoint(x: 30.11, y: 12.01))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 30.98, y: 17.48), controlPoint1: CGPoint(x: 30.96, y: 17.17), controlPoint2: CGPoint(x: 30.97, y: 17.32))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 32.44, y: 19.33), controlPoint1: CGPoint(x: 31.02, y: 18.08), controlPoint2: CGPoint(x: 31.1, y: 19.33))
|
||||
bezier5Path.addLine(to: CGPoint(x: 32.44, y: 19.33))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 32.8, y: 19.3), controlPoint1: CGPoint(x: 32.55, y: 19.33), controlPoint2: CGPoint(x: 32.68, y: 19.32))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 34, y: 17.42), controlPoint1: CGPoint(x: 34.05, y: 19.11), controlPoint2: CGPoint(x: 34.02, y: 17.97))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 34, y: 17.02), controlPoint1: CGPoint(x: 33.99, y: 17.26), controlPoint2: CGPoint(x: 33.99, y: 17.12))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 34, y: 16.95), controlPoint1: CGPoint(x: 34, y: 17), controlPoint2: CGPoint(x: 34, y: 16.97))
|
||||
bezier5Path.close()
|
||||
bezier5Path.move(to: CGPoint(x: 22.28, y: 4.03))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 22.69, y: 4.07), controlPoint1: CGPoint(x: 22.43, y: 4.04), controlPoint2: CGPoint(x: 22.57, y: 4.05))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 35.93, y: 17.71), controlPoint1: CGPoint(x: 31.03, y: 5.35), controlPoint2: CGPoint(x: 34.87, y: 9.31))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 35.95, y: 18.22), controlPoint1: CGPoint(x: 35.95, y: 17.86), controlPoint2: CGPoint(x: 35.95, y: 18.03))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 37.45, y: 20.27), controlPoint1: CGPoint(x: 35.97, y: 18.87), controlPoint2: CGPoint(x: 35.99, y: 20.24))
|
||||
bezier5Path.addLine(to: CGPoint(x: 37.5, y: 20.27))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 38.59, y: 19.85), controlPoint1: CGPoint(x: 37.96, y: 20.27), controlPoint2: CGPoint(x: 38.33, y: 20.13))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 38.99, y: 18.1), controlPoint1: CGPoint(x: 39.04, y: 19.38), controlPoint2: CGPoint(x: 39.01, y: 18.67))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 38.98, y: 17.71), controlPoint1: CGPoint(x: 38.98, y: 17.96), controlPoint2: CGPoint(x: 38.98, y: 17.82))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 23.05, y: 1.02), controlPoint1: CGPoint(x: 39.08, y: 9.11), controlPoint2: CGPoint(x: 31.64, y: 1.31))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 22.95, y: 1.03), controlPoint1: CGPoint(x: 23.01, y: 1.02), controlPoint2: CGPoint(x: 22.98, y: 1.02))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 22.84, y: 1.03), controlPoint1: CGPoint(x: 22.93, y: 1.03), controlPoint2: CGPoint(x: 22.9, y: 1.03))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 22.54, y: 1.02), controlPoint1: CGPoint(x: 22.76, y: 1.03), controlPoint2: CGPoint(x: 22.65, y: 1.03))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 22.1, y: 1), controlPoint1: CGPoint(x: 22.41, y: 1.01), controlPoint2: CGPoint(x: 22.25, y: 1))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 20.44, y: 2.55), controlPoint1: CGPoint(x: 20.73, y: 1), controlPoint2: CGPoint(x: 20.47, y: 1.97))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 22.28, y: 4.03), controlPoint1: CGPoint(x: 20.36, y: 3.89), controlPoint2: CGPoint(x: 21.66, y: 3.99))
|
||||
bezier5Path.close()
|
||||
bezier5Path.move(to: CGPoint(x: 35.53, y: 28.58))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 35, y: 28.17), controlPoint1: CGPoint(x: 35.35, y: 28.44), controlPoint2: CGPoint(x: 35.17, y: 28.3))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 32.18, y: 26.1), controlPoint1: CGPoint(x: 34.09, y: 27.43), controlPoint2: CGPoint(x: 33.12, y: 26.76))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 31.6, y: 25.7), controlPoint1: CGPoint(x: 31.98, y: 25.97), controlPoint2: CGPoint(x: 31.79, y: 25.83))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 28.3, y: 24.44), controlPoint1: CGPoint(x: 30.4, y: 24.85), controlPoint2: CGPoint(x: 29.32, y: 24.44))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 24.74, y: 26.7), controlPoint1: CGPoint(x: 26.92, y: 24.44), controlPoint2: CGPoint(x: 25.73, y: 25.2))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 23.12, y: 27.68), controlPoint1: CGPoint(x: 24.3, y: 27.36), controlPoint2: CGPoint(x: 23.77, y: 27.68))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 21.81, y: 27.36), controlPoint1: CGPoint(x: 22.73, y: 27.68), controlPoint2: CGPoint(x: 22.29, y: 27.57))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 13.59, y: 19.35), controlPoint1: CGPoint(x: 17.93, y: 25.6), controlPoint2: CGPoint(x: 15.17, y: 22.91))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 14.42, y: 15.59), controlPoint1: CGPoint(x: 12.82, y: 17.63), controlPoint2: CGPoint(x: 13.07, y: 16.51))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 16.5, y: 12.26), controlPoint1: CGPoint(x: 15.18, y: 15.07), controlPoint2: CGPoint(x: 16.6, y: 14.11))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 9.8, y: 3.12), controlPoint1: CGPoint(x: 16.39, y: 10.16), controlPoint2: CGPoint(x: 11.75, y: 3.84))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 7.22, y: 3.11), controlPoint1: CGPoint(x: 8.97, y: 2.82), controlPoint2: CGPoint(x: 8.11, y: 2.81))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 2.56, y: 6.95), controlPoint1: CGPoint(x: 4.97, y: 3.87), controlPoint2: CGPoint(x: 3.36, y: 5.19))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 2.66, y: 12.7), controlPoint1: CGPoint(x: 1.78, y: 8.64), controlPoint2: CGPoint(x: 1.82, y: 10.63))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 12.84, y: 28.19), controlPoint1: CGPoint(x: 5.09, y: 18.67), controlPoint2: CGPoint(x: 8.52, y: 23.89))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 28.27, y: 38.44), controlPoint1: CGPoint(x: 17.06, y: 32.4), controlPoint2: CGPoint(x: 22.26, y: 35.85))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 29.8, y: 38.9), controlPoint1: CGPoint(x: 28.82, y: 38.68), controlPoint2: CGPoint(x: 29.39, y: 38.8))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 30.15, y: 38.98), controlPoint1: CGPoint(x: 29.94, y: 38.93), controlPoint2: CGPoint(x: 30.07, y: 38.95))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 30.3, y: 39), controlPoint1: CGPoint(x: 30.2, y: 38.99), controlPoint2: CGPoint(x: 30.25, y: 39))
|
||||
bezier5Path.addLine(to: CGPoint(x: 30.35, y: 39))
|
||||
bezier5Path.addLine(to: CGPoint(x: 30.35, y: 39))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 37.63, y: 33.46), controlPoint1: CGPoint(x: 33.18, y: 39), controlPoint2: CGPoint(x: 36.58, y: 36.41))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 35.53, y: 28.58), controlPoint1: CGPoint(x: 38.54, y: 30.88), controlPoint2: CGPoint(x: 36.87, y: 29.61))
|
||||
bezier5Path.close()
|
||||
bezier5Path.move(to: CGPoint(x: 23.53, y: 10.86))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 21.68, y: 11.93), controlPoint1: CGPoint(x: 23.05, y: 10.87), controlPoint2: CGPoint(x: 22.04, y: 10.9))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 21.74, y: 13.16), controlPoint1: CGPoint(x: 21.52, y: 12.41), controlPoint2: CGPoint(x: 21.54, y: 12.82))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 23.14, y: 13.9), controlPoint1: CGPoint(x: 22.04, y: 13.67), controlPoint2: CGPoint(x: 22.62, y: 13.82))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 26.21, y: 17.21), controlPoint1: CGPoint(x: 25.04, y: 14.21), controlPoint2: CGPoint(x: 26.02, y: 15.26))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 27.7, y: 18.75), controlPoint1: CGPoint(x: 26.3, y: 18.12), controlPoint2: CGPoint(x: 26.91, y: 18.75))
|
||||
bezier5Path.addLine(to: CGPoint(x: 27.7, y: 18.75))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 27.88, y: 18.74), controlPoint1: CGPoint(x: 27.76, y: 18.75), controlPoint2: CGPoint(x: 27.82, y: 18.75))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 29.24, y: 16.68), controlPoint1: CGPoint(x: 28.82, y: 18.63), controlPoint2: CGPoint(x: 29.28, y: 17.94))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 27.4, y: 12.69), controlPoint1: CGPoint(x: 29.25, y: 15.37), controlPoint2: CGPoint(x: 28.57, y: 13.88))
|
||||
bezier5Path.addCurve(to: CGPoint(x: 23.53, y: 10.86), controlPoint1: CGPoint(x: 26.23, y: 11.5), controlPoint2: CGPoint(x: 24.82, y: 10.83))
|
||||
bezier5Path.close()
|
||||
fillColor.setFill()
|
||||
bezier5Path.fill()
|
||||
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
@objc(SocialIconStyleKitResizingBehavior)
|
||||
public enum ResizingBehavior: Int {
|
||||
case aspectFit /// The content is proportionally resized to fit into the target rectangle.
|
||||
case aspectFill /// The content is proportionally resized to completely fill the target rectangle.
|
||||
case stretch /// The content is stretched to match the entire target rectangle.
|
||||
case center /// The content is centered in the target rectangle, but it is NOT resized.
|
||||
|
||||
public func apply(rect: CGRect, target: CGRect) -> CGRect {
|
||||
if rect == target || target == CGRect.zero {
|
||||
return rect
|
||||
}
|
||||
|
||||
var scales = CGSize.zero
|
||||
scales.width = abs(target.width / rect.width)
|
||||
scales.height = abs(target.height / rect.height)
|
||||
|
||||
switch self {
|
||||
case .aspectFit:
|
||||
scales.width = min(scales.width, scales.height)
|
||||
scales.height = scales.width
|
||||
case .aspectFill:
|
||||
scales.width = max(scales.width, scales.height)
|
||||
scales.height = scales.width
|
||||
case .stretch:
|
||||
break
|
||||
case .center:
|
||||
scales.width = 1
|
||||
scales.height = 1
|
||||
}
|
||||
|
||||
var result = rect.standardized
|
||||
result.size.width *= scales.width
|
||||
result.size.height *= scales.height
|
||||
result.origin.x = target.minX + (target.width - result.width) / 2
|
||||
result.origin.y = target.minY + (target.height - result.height) / 2
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
private override init() {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension SPCodeDraw {
|
||||
|
||||
public class SystemIconPack : NSObject {
|
||||
|
||||
private struct Cache {
|
||||
static let gradient: CGGradient = CGGradient(colorsSpace: nil, colors: [UIColor.red.cgColor, UIColor.red.cgColor] as CFArray, locations: [0, 1])!
|
||||
}
|
||||
|
||||
@objc dynamic public class var gradient: CGGradient { return Cache.gradient }
|
||||
|
||||
@objc dynamic public class func drawFavorite(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
context.saveGState()
|
||||
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 100, height: 100), target: targetFrame)
|
||||
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
|
||||
context.scaleBy(x: resizedFrame.width / 100, y: resizedFrame.height / 100)
|
||||
|
||||
let bezier2Path = UIBezierPath()
|
||||
bezier2Path.move(to: CGPoint(x: 49.5, y: 8.22))
|
||||
bezier2Path.addLine(to: CGPoint(x: 39.17, y: 40.29))
|
||||
bezier2Path.addLine(to: CGPoint(x: 36.66, y: 40.32))
|
||||
bezier2Path.addLine(to: CGPoint(x: 6.89, y: 40.68))
|
||||
bezier2Path.addLine(to: CGPoint(x: 32.78, y: 60.88))
|
||||
bezier2Path.addLine(to: CGPoint(x: 32.04, y: 63.39))
|
||||
bezier2Path.addLine(to: CGPoint(x: 23.17, y: 93.18))
|
||||
bezier2Path.addLine(to: CGPoint(x: 49.5, y: 73.61))
|
||||
bezier2Path.addLine(to: CGPoint(x: 51.55, y: 75.13))
|
||||
bezier2Path.addLine(to: CGPoint(x: 75.83, y: 93.19))
|
||||
bezier2Path.addLine(to: CGPoint(x: 66.22, y: 60.89))
|
||||
bezier2Path.addLine(to: CGPoint(x: 68.23, y: 59.32))
|
||||
bezier2Path.addLine(to: CGPoint(x: 92.11, y: 40.68))
|
||||
bezier2Path.addLine(to: CGPoint(x: 59.83, y: 40.29))
|
||||
bezier2Path.addLine(to: CGPoint(x: 49.5, y: 8.22))
|
||||
bezier2Path.close()
|
||||
bezier2Path.move(to: CGPoint(x: 52.21, y: 5.04))
|
||||
bezier2Path.addLine(to: CGPoint(x: 62.38, y: 36.61))
|
||||
bezier2Path.addLine(to: CGPoint(x: 94.17, y: 37))
|
||||
bezier2Path.addCurve(to: CGPoint(x: 95.84, y: 42.39), controlPoint1: CGPoint(x: 96.9, y: 37.03), controlPoint2: CGPoint(x: 98.04, y: 40.68))
|
||||
bezier2Path.addLine(to: CGPoint(x: 70.34, y: 62.29))
|
||||
bezier2Path.addLine(to: CGPoint(x: 79.81, y: 94.1))
|
||||
bezier2Path.addCurve(to: CGPoint(x: 75.43, y: 97.43), controlPoint1: CGPoint(x: 80.63, y: 96.84), controlPoint2: CGPoint(x: 77.67, y: 99.09))
|
||||
bezier2Path.addLine(to: CGPoint(x: 49.5, y: 78.16))
|
||||
bezier2Path.addLine(to: CGPoint(x: 23.57, y: 97.43))
|
||||
bezier2Path.addCurve(to: CGPoint(x: 19.19, y: 94.1), controlPoint1: CGPoint(x: 21.34, y: 99.09), controlPoint2: CGPoint(x: 18.37, y: 96.83))
|
||||
bezier2Path.addLine(to: CGPoint(x: 28.66, y: 62.29))
|
||||
bezier2Path.addLine(to: CGPoint(x: 3.16, y: 42.39))
|
||||
bezier2Path.addCurve(to: CGPoint(x: 4.83, y: 36.99), controlPoint1: CGPoint(x: 0.96, y: 40.68), controlPoint2: CGPoint(x: 2.1, y: 37.03))
|
||||
bezier2Path.addLine(to: CGPoint(x: 36.62, y: 36.61))
|
||||
bezier2Path.addLine(to: CGPoint(x: 46.79, y: 5.04))
|
||||
bezier2Path.addCurve(to: CGPoint(x: 52.21, y: 5.04), controlPoint1: CGPoint(x: 47.67, y: 2.32), controlPoint2: CGPoint(x: 51.33, y: 2.32))
|
||||
bezier2Path.close()
|
||||
color.setFill()
|
||||
bezier2Path.fill()
|
||||
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawFavoriteFill(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
context.saveGState()
|
||||
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 100, height: 100), target: targetFrame)
|
||||
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
|
||||
context.scaleBy(x: resizedFrame.width / 100, y: resizedFrame.height / 100)
|
||||
|
||||
let bezierPath = UIBezierPath()
|
||||
bezierPath.move(to: CGPoint(x: 51.78, y: 4.92))
|
||||
bezierPath.addLine(to: CGPoint(x: 61.96, y: 36.5))
|
||||
bezierPath.addLine(to: CGPoint(x: 93.75, y: 36.88))
|
||||
bezierPath.addCurve(to: CGPoint(x: 95.42, y: 42.28), controlPoint1: CGPoint(x: 96.48, y: 36.92), controlPoint2: CGPoint(x: 97.61, y: 40.57))
|
||||
bezierPath.addLine(to: CGPoint(x: 69.92, y: 62.17))
|
||||
bezierPath.addLine(to: CGPoint(x: 79.39, y: 93.98))
|
||||
bezierPath.addCurve(to: CGPoint(x: 75.01, y: 97.32), controlPoint1: CGPoint(x: 80.2, y: 96.72), controlPoint2: CGPoint(x: 77.24, y: 98.98))
|
||||
bezierPath.addLine(to: CGPoint(x: 49.08, y: 78.04))
|
||||
bezierPath.addLine(to: CGPoint(x: 23.14, y: 97.32))
|
||||
bezierPath.addCurve(to: CGPoint(x: 18.76, y: 93.98), controlPoint1: CGPoint(x: 20.91, y: 98.97), controlPoint2: CGPoint(x: 17.95, y: 96.71))
|
||||
bezierPath.addLine(to: CGPoint(x: 28.24, y: 62.17))
|
||||
bezierPath.addLine(to: CGPoint(x: 2.73, y: 42.27))
|
||||
bezierPath.addCurve(to: CGPoint(x: 4.41, y: 36.88), controlPoint1: CGPoint(x: 0.54, y: 40.57), controlPoint2: CGPoint(x: 1.67, y: 36.91))
|
||||
bezierPath.addLine(to: CGPoint(x: 36.19, y: 36.49))
|
||||
bezierPath.addLine(to: CGPoint(x: 46.37, y: 4.92))
|
||||
bezierPath.addCurve(to: CGPoint(x: 51.78, y: 4.92), controlPoint1: CGPoint(x: 47.24, y: 2.2), controlPoint2: CGPoint(x: 50.91, y: 2.2))
|
||||
bezierPath.close()
|
||||
color.setFill()
|
||||
bezierPath.fill()
|
||||
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawShare(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
context.saveGState()
|
||||
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 100, height: 100), target: targetFrame)
|
||||
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
|
||||
context.scaleBy(x: resizedFrame.width / 100, y: resizedFrame.height / 100)
|
||||
|
||||
let clip2Path = UIBezierPath()
|
||||
clip2Path.move(to: CGPoint(x: 38.59, y: 33.54))
|
||||
clip2Path.addLine(to: CGPoint(x: 38.59, y: 38.16))
|
||||
clip2Path.addLine(to: CGPoint(x: 24.9, y: 38.16))
|
||||
clip2Path.addLine(to: CGPoint(x: 24.9, y: 90.08))
|
||||
clip2Path.addLine(to: CGPoint(x: 75.1, y: 90.08))
|
||||
clip2Path.addLine(to: CGPoint(x: 75.1, y: 38.16))
|
||||
clip2Path.addLine(to: CGPoint(x: 61.41, y: 38.16))
|
||||
clip2Path.addLine(to: CGPoint(x: 61.41, y: 33.54))
|
||||
clip2Path.addLine(to: CGPoint(x: 80, y: 33.54))
|
||||
clip2Path.addLine(to: CGPoint(x: 80, y: 95))
|
||||
clip2Path.addLine(to: CGPoint(x: 20, y: 95))
|
||||
clip2Path.addLine(to: CGPoint(x: 20, y: 33.54))
|
||||
clip2Path.addLine(to: CGPoint(x: 38.59, y: 33.54))
|
||||
clip2Path.close()
|
||||
clip2Path.move(to: CGPoint(x: 52.27, y: 61.81))
|
||||
clip2Path.addLine(to: CGPoint(x: 47.73, y: 61.81))
|
||||
clip2Path.addLine(to: CGPoint(x: 47.73, y: 14.88))
|
||||
clip2Path.addLine(to: CGPoint(x: 40.08, y: 22.75))
|
||||
clip2Path.addLine(to: CGPoint(x: 37.14, y: 19.73))
|
||||
clip2Path.addLine(to: CGPoint(x: 50, y: 6.5))
|
||||
clip2Path.addLine(to: CGPoint(x: 62.86, y: 19.73))
|
||||
clip2Path.addLine(to: CGPoint(x: 59.92, y: 22.75))
|
||||
clip2Path.addLine(to: CGPoint(x: 52.27, y: 14.88))
|
||||
clip2Path.addLine(to: CGPoint(x: 52.27, y: 61.81))
|
||||
clip2Path.close()
|
||||
color.setFill()
|
||||
clip2Path.fill()
|
||||
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawClose(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
context.saveGState()
|
||||
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 100, height: 100), target: targetFrame)
|
||||
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
|
||||
context.scaleBy(x: resizedFrame.width / 100, y: resizedFrame.height / 100)
|
||||
|
||||
let bezierPath = UIBezierPath()
|
||||
bezierPath.move(to: CGPoint(x: 92.02, y: 22.92))
|
||||
bezierPath.addLine(to: CGPoint(x: 64.42, y: 50.52))
|
||||
bezierPath.addLine(to: CGPoint(x: 92.02, y: 78.13))
|
||||
bezierPath.addCurve(to: CGPoint(x: 92.02, y: 92.99), controlPoint1: CGPoint(x: 96.13, y: 82.23), controlPoint2: CGPoint(x: 96.13, y: 88.89))
|
||||
bezierPath.addCurve(to: CGPoint(x: 84.59, y: 96.07), controlPoint1: CGPoint(x: 89.97, y: 95.05), controlPoint2: CGPoint(x: 87.28, y: 96.07))
|
||||
bezierPath.addCurve(to: CGPoint(x: 77.16, y: 92.99), controlPoint1: CGPoint(x: 81.9, y: 96.07), controlPoint2: CGPoint(x: 79.22, y: 95.05))
|
||||
bezierPath.addLine(to: CGPoint(x: 49.55, y: 65.38))
|
||||
bezierPath.addLine(to: CGPoint(x: 21.95, y: 92.99))
|
||||
bezierPath.addCurve(to: CGPoint(x: 14.51, y: 96.07), controlPoint1: CGPoint(x: 19.89, y: 95.05), controlPoint2: CGPoint(x: 17.2, y: 96.07))
|
||||
bezierPath.addCurve(to: CGPoint(x: 7.08, y: 92.99), controlPoint1: CGPoint(x: 11.82, y: 96.07), controlPoint2: CGPoint(x: 9.13, y: 95.05))
|
||||
bezierPath.addCurve(to: CGPoint(x: 7.08, y: 78.13), controlPoint1: CGPoint(x: 2.97, y: 88.89), controlPoint2: CGPoint(x: 2.97, y: 82.23))
|
||||
bezierPath.addLine(to: CGPoint(x: 34.69, y: 50.52))
|
||||
bezierPath.addLine(to: CGPoint(x: 7.08, y: 22.92))
|
||||
bezierPath.addCurve(to: CGPoint(x: 7.08, y: 8.04), controlPoint1: CGPoint(x: 2.97, y: 18.8), controlPoint2: CGPoint(x: 2.97, y: 12.15))
|
||||
bezierPath.addCurve(to: CGPoint(x: 21.94, y: 8.04), controlPoint1: CGPoint(x: 11.18, y: 3.94), controlPoint2: CGPoint(x: 17.84, y: 3.94))
|
||||
bezierPath.addLine(to: CGPoint(x: 49.55, y: 35.65))
|
||||
bezierPath.addLine(to: CGPoint(x: 77.16, y: 8.04))
|
||||
bezierPath.addCurve(to: CGPoint(x: 92.02, y: 8.04), controlPoint1: CGPoint(x: 81.26, y: 3.94), controlPoint2: CGPoint(x: 87.92, y: 3.94))
|
||||
bezierPath.addCurve(to: CGPoint(x: 92.02, y: 22.92), controlPoint1: CGPoint(x: 96.13, y: 12.15), controlPoint2: CGPoint(x: 96.13, y: 18.8))
|
||||
bezierPath.close()
|
||||
color.setFill()
|
||||
bezierPath.fill()
|
||||
|
||||
context.restoreGState()
|
||||
|
||||
}
|
||||
|
||||
@objc(StyleKitNameResizingBehavior)
|
||||
public enum ResizingBehavior: Int {
|
||||
case aspectFit /// The content is proportionally resized to fit into the target rectangle.
|
||||
case aspectFill /// The content is proportionally resized to completely fill the target rectangle.
|
||||
case stretch /// The content is stretched to match the entire target rectangle.
|
||||
case center /// The content is centered in the target rectangle, but it is NOT resized.
|
||||
|
||||
public func apply(rect: CGRect, target: CGRect) -> CGRect {
|
||||
if rect == target || target == CGRect.zero {
|
||||
return rect
|
||||
}
|
||||
|
||||
var scales = CGSize.zero
|
||||
scales.width = abs(target.width / rect.width)
|
||||
scales.height = abs(target.height / rect.height)
|
||||
|
||||
switch self {
|
||||
case .aspectFit:
|
||||
scales.width = min(scales.width, scales.height)
|
||||
scales.height = scales.width
|
||||
case .aspectFill:
|
||||
scales.width = max(scales.width, scales.height)
|
||||
scales.height = scales.width
|
||||
case .stretch:
|
||||
break
|
||||
case .center:
|
||||
scales.width = 1
|
||||
scales.height = 1
|
||||
}
|
||||
|
||||
var result = rect.standardized
|
||||
result.size.width *= scales.width
|
||||
result.size.height *= scales.height
|
||||
result.origin.x = target.minX + (target.width - result.width) / 2
|
||||
result.origin.y = target.minY + (target.height - result.height) / 2
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
private override init() {}
|
||||
}
|
||||
}
|
||||
+8294
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public struct SPConstraints {
|
||||
|
||||
static func setEqualSize(_ view: UIView, superVuew: UIView) {
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
view.topAnchor.constraint(equalTo: superVuew.topAnchor),
|
||||
view.leftAnchor.constraint(equalTo: superVuew.leftAnchor),
|
||||
view.rightAnchor.constraint(equalTo: superVuew.rightAnchor),
|
||||
view.bottomAnchor.constraint(equalTo: superVuew.bottomAnchor)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
|
||||
public func delay(_ delay:Double, closure:@escaping ()->()) {
|
||||
let when = DispatchTime.now() + delay
|
||||
DispatchQueue.main.asyncAfter(deadline: when) {
|
||||
closure()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
struct SPDevice {
|
||||
|
||||
static var isIphone: Bool {
|
||||
return UIDevice.current.userInterfaceIdiom == .phone
|
||||
}
|
||||
|
||||
static var isIpad: Bool {
|
||||
return UIDevice.current.userInterfaceIdiom == .pad
|
||||
}
|
||||
|
||||
struct Orientation {
|
||||
|
||||
static var isPortrait: Bool {
|
||||
var isPortraitOrientation = true
|
||||
if UIDevice.current.orientation.isValidInterfaceOrientation {
|
||||
if UIDevice.current.orientation.isPortrait {
|
||||
isPortraitOrientation = true
|
||||
} else {
|
||||
isPortraitOrientation = false
|
||||
}
|
||||
} else {
|
||||
if UIScreen.main.bounds.width < UIScreen.main.bounds.height {
|
||||
isPortraitOrientation = true
|
||||
} else {
|
||||
isPortraitOrientation = false
|
||||
}
|
||||
}
|
||||
return isPortraitOrientation
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
struct SPDownloader {
|
||||
|
||||
static func image(link: String, withComplection complection: @escaping (UIImage?) -> ()) {
|
||||
guard let url = URL(string: link) else {
|
||||
DispatchQueue.main.async {
|
||||
complection(nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
URLSession.shared.dataTask(with: url) { (data, response, error) in
|
||||
guard
|
||||
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
|
||||
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
|
||||
let data = data, error == nil,
|
||||
let image = UIImage(data: data)
|
||||
else {
|
||||
DispatchQueue.main.async {
|
||||
complection(nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
complection(image)
|
||||
}
|
||||
}.resume()
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Array {
|
||||
|
||||
func get(count: Int) -> Array {
|
||||
if (count < self.count) { return Array(self[0..<count]) }
|
||||
return Array(self)
|
||||
}
|
||||
}
|
||||
|
||||
extension Array where Element: Equatable {
|
||||
|
||||
mutating func removeDuplicates() {
|
||||
var result = [Element]()
|
||||
for value in self {
|
||||
if result.contains(value) == false { result.append(value) }
|
||||
}
|
||||
self = result
|
||||
}
|
||||
}
|
||||
|
||||
extension Array where Element: Hashable {
|
||||
|
||||
func after(item: Element) -> Element? {
|
||||
if let index = self.index(of: item), index + 1 < self.count { return self[index + 1] }
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension CGRect {
|
||||
|
||||
var bottomXPosition: CGFloat {
|
||||
get { return self.origin.x + self.width }
|
||||
set { self.origin.x = newValue - self.width }
|
||||
}
|
||||
|
||||
var bottomYPosition: CGFloat {
|
||||
get { return self.origin.y + self.height }
|
||||
set { self.origin.y = newValue - self.height }
|
||||
}
|
||||
|
||||
var minSideSize: CGFloat {
|
||||
return min(self.width, self.height)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension CGSize {
|
||||
|
||||
func resize(width: CGFloat) -> CGSize {
|
||||
let relativeSideSize = self.width / self.height
|
||||
let newHeight = width / relativeSideSize
|
||||
return CGSize.init(width: width, height: newHeight)
|
||||
}
|
||||
|
||||
func resize(height: CGFloat) -> CGSize {
|
||||
let relativeSideSize = self.width / self.height
|
||||
let newWidth = height * relativeSideSize
|
||||
return CGSize.init(width: newWidth, height: height)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Strideable {
|
||||
|
||||
public mutating func setIfMore(when value: Self) {
|
||||
if self > value {
|
||||
self = value
|
||||
}
|
||||
}
|
||||
|
||||
public mutating func setIfFewer(when value: Self) {
|
||||
if self < value {
|
||||
self = value
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
extension String {
|
||||
|
||||
mutating func dropLast(substring: String) {
|
||||
if self.hasSuffix(substring) {
|
||||
self = String(dropLast(substring.count))
|
||||
}
|
||||
}
|
||||
|
||||
mutating func dropFirst(substring: String) {
|
||||
if self.hasPrefix(substring) {
|
||||
self = String(dropFirst(substring.count))
|
||||
}
|
||||
}
|
||||
|
||||
func uppercasedFirstLetter() -> String {
|
||||
let lowercaseSctring = self.lowercased()
|
||||
return lowercaseSctring.prefix(1).uppercased() + lowercaseSctring.dropFirst()
|
||||
}
|
||||
|
||||
mutating func uppercaseFirstLetter() {
|
||||
self = self.uppercasedFirstLetter()
|
||||
}
|
||||
|
||||
func removeAllSpaces() -> String {
|
||||
return self.components(separatedBy: .whitespaces).joined()
|
||||
}
|
||||
|
||||
mutating func removeAllSpaces() {
|
||||
self = self.removeAllSpaces()
|
||||
}
|
||||
|
||||
var isEmail: Bool {
|
||||
let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
|
||||
let emailTest = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
|
||||
return emailTest.evaluate(with: self)
|
||||
}
|
||||
|
||||
var isLink: Bool {
|
||||
if let url = URL(string: self) {
|
||||
return UIApplication.shared.canOpenURL(url)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
mutating func replace(_ replacingString: String, with newString: String) {
|
||||
self = self.replacingOccurrences(of: replacingString, with: newString)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UITextField {
|
||||
|
||||
@IBInspectable var placeholderColor: UIColor? {
|
||||
get {
|
||||
return self.placeholderColor
|
||||
}
|
||||
set {
|
||||
self.attributedPlaceholder = NSAttributedString(string:self.placeholder != nil ? self.placeholder! : "", attributes:[NSAttributedString.Key.foregroundColor: newValue!])
|
||||
}
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIAlertController {
|
||||
|
||||
static var elementsColor: UIColor {
|
||||
get {
|
||||
return UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor
|
||||
}
|
||||
set {
|
||||
UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor = newValue
|
||||
}
|
||||
}
|
||||
|
||||
public static func show(title: String, message: String, buttonTitle: String, cancelButtonTitle: String? = nil, complection: @escaping ()->() = {}, on viewController: UIViewController) {
|
||||
let ac = UIAlertController(
|
||||
title: title,
|
||||
message: message,
|
||||
preferredStyle: .alert
|
||||
)
|
||||
|
||||
if cancelButtonTitle != nil {
|
||||
ac.addAction(UIAlertAction.init(
|
||||
title: cancelButtonTitle!,
|
||||
style: UIAlertAction.Style.cancel,
|
||||
handler: nil)
|
||||
)
|
||||
}
|
||||
|
||||
ac.addAction(UIAlertAction.init(
|
||||
title: buttonTitle,
|
||||
style: UIAlertAction.Style.default,
|
||||
handler: { (action) in
|
||||
complection()
|
||||
}))
|
||||
|
||||
viewController.present(ac, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
public static func сonfirm(title: String? = nil, message: String, buttonTitle: String, cancelButtonTitle: String, isDestructive: Bool = false, complection: @escaping (Bool)->(), on viewController: UIViewController) {
|
||||
let ac = UIAlertController(
|
||||
title: title,
|
||||
message: message,
|
||||
preferredStyle: .actionSheet
|
||||
)
|
||||
|
||||
var style = UIAlertAction.Style.default
|
||||
if isDestructive {
|
||||
style = .destructive
|
||||
}
|
||||
ac.addAction(UIAlertAction.init(
|
||||
title: buttonTitle,
|
||||
style: style,
|
||||
handler: { (action) in
|
||||
complection(true)
|
||||
}))
|
||||
ac.addAction(UIAlertAction.init(
|
||||
title: cancelButtonTitle,
|
||||
style: UIAlertAction.Style.default,
|
||||
handler: { (action) in
|
||||
complection(false)
|
||||
}))
|
||||
|
||||
viewController.present(ac, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIAlertController {
|
||||
|
||||
func addAction(title: String, complection: @escaping ()->()) {
|
||||
let action = UIAlertAction(title: title, style: .default) { (action) in
|
||||
complection()
|
||||
}
|
||||
self.addAction(action)
|
||||
}
|
||||
|
||||
func addDestructiveAction(title: String, complection: @escaping ()->()) {
|
||||
let action = UIAlertAction(title: title, style: .destructive) { (action) in
|
||||
complection()
|
||||
}
|
||||
self.addAction(action)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIButton {
|
||||
|
||||
typealias UIButtonTargetClosure = () -> ()
|
||||
|
||||
private class ClosureWrapper: NSObject {
|
||||
let closure: UIButtonTargetClosure
|
||||
init(_ closure: @escaping UIButtonTargetClosure) {
|
||||
self.closure = closure
|
||||
}
|
||||
}
|
||||
|
||||
private struct AssociatedKeys {
|
||||
static var targetClosure = "targetClosure"
|
||||
}
|
||||
|
||||
private var targetClosure: UIButtonTargetClosure? {
|
||||
get {
|
||||
guard let closureWrapper = objc_getAssociatedObject(self, &AssociatedKeys.targetClosure) as? ClosureWrapper else { return nil }
|
||||
return closureWrapper.closure
|
||||
}
|
||||
set(newValue) {
|
||||
guard let newValue = newValue else { return }
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.targetClosure, ClosureWrapper(newValue), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
func target(_ action: @escaping UIButtonTargetClosure) {
|
||||
targetClosure = action
|
||||
addTarget(self, action: #selector(UIButton.targetAction), for: .touchUpInside)
|
||||
}
|
||||
|
||||
@objc func targetAction() {
|
||||
guard let targetClosure = targetClosure else { return }
|
||||
targetClosure()
|
||||
}
|
||||
}
|
||||
|
||||
extension UIButton {
|
||||
|
||||
func setTitle(_ title: String) {
|
||||
self.setTitle(title, for: .normal)
|
||||
}
|
||||
|
||||
func setTitleColor(_ color: UIColor) {
|
||||
self.setTitleColor(color, for: .normal)
|
||||
self.setTitleColor(color.withAlphaComponent(0.7), for: .highlighted)
|
||||
}
|
||||
|
||||
func removeAllTargets() {
|
||||
self.removeTarget(nil, action: nil, for: .allEvents)
|
||||
}
|
||||
|
||||
func showText(_ text: String, withComplection completion: (() -> Void)! = {}) {
|
||||
let baseText = self.titleLabel?.text ?? " "
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
self.titleLabel?.alpha = 0
|
||||
}, withComplection: {
|
||||
self.setTitle(text, for: .normal)
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
self.titleLabel?.alpha = 1
|
||||
}, withComplection: {
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
self.titleLabel?.alpha = 0
|
||||
}, delay: 0.35,
|
||||
withComplection: {
|
||||
self.setTitle(baseText, for: .normal)
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
self.titleLabel?.alpha = 1
|
||||
}, withComplection: {
|
||||
completion()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func setAnimatableText(_ text: String, withComplection completion: (() -> Void)! = {}) {
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
self.titleLabel?.alpha = 0
|
||||
}, withComplection: {
|
||||
self.setTitle(text, for: .normal)
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
self.titleLabel?.alpha = 1
|
||||
}, withComplection: {
|
||||
completion()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func hideContent(completion: (() -> Void)! = {}) {
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
self.titleLabel?.alpha = 0
|
||||
}, withComplection: {
|
||||
completion()
|
||||
})
|
||||
}
|
||||
|
||||
func showContent(completion: (() -> Void)! = {}) {
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
self.titleLabel?.alpha = 1
|
||||
}, withComplection: {
|
||||
completion()
|
||||
})
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UICollectionView {
|
||||
|
||||
var currentIndexCellPath: IndexPath? {
|
||||
let visibleRect = CGRect(origin: self.contentOffset, size: self.bounds.size)
|
||||
let visiblePoint = CGPoint.init(x: visibleRect.midX, y: visibleRect.midY)
|
||||
let visibleIndexPath = self.indexPathForItem(at: visiblePoint)
|
||||
return visibleIndexPath
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIColor {
|
||||
|
||||
convenience init(hex: String) {
|
||||
var red: CGFloat = 0.0
|
||||
var green: CGFloat = 0.0
|
||||
var blue: CGFloat = 0.0
|
||||
var alpha: CGFloat = 1.0
|
||||
var hex: String = hex
|
||||
|
||||
if hex.hasPrefix("#") {
|
||||
let index = hex.index(hex.startIndex, offsetBy: 1)
|
||||
hex = String(hex[index...])
|
||||
}
|
||||
|
||||
let scanner = Scanner(string: hex)
|
||||
var hexValue: CUnsignedLongLong = 0
|
||||
if scanner.scanHexInt64(&hexValue) {
|
||||
switch (hex.count) {
|
||||
case 3:
|
||||
red = CGFloat((hexValue & 0xF00) >> 8) / 15.0
|
||||
green = CGFloat((hexValue & 0x0F0) >> 4) / 15.0
|
||||
blue = CGFloat(hexValue & 0x00F) / 15.0
|
||||
case 4:
|
||||
red = CGFloat((hexValue & 0xF000) >> 12) / 15.0
|
||||
green = CGFloat((hexValue & 0x0F00) >> 8) / 15.0
|
||||
blue = CGFloat((hexValue & 0x00F0) >> 4) / 15.0
|
||||
alpha = CGFloat(hexValue & 0x000F) / 15.0
|
||||
case 6:
|
||||
red = CGFloat((hexValue & 0xFF0000) >> 16) / 255.0
|
||||
green = CGFloat((hexValue & 0x00FF00) >> 8) / 255.0
|
||||
blue = CGFloat(hexValue & 0x0000FF) / 255.0
|
||||
case 8:
|
||||
red = CGFloat((hexValue & 0xFF000000) >> 24) / 255.0
|
||||
green = CGFloat((hexValue & 0x00FF0000) >> 16) / 255.0
|
||||
blue = CGFloat((hexValue & 0x0000FF00) >> 8) / 255.0
|
||||
alpha = CGFloat(hexValue & 0x000000FF) / 255.0
|
||||
default:
|
||||
print("SPUIColorExtension - Invalid RGB string, number of characters after '#' should be either 3, 4, 6 or 8", terminator: "")
|
||||
}
|
||||
} else {
|
||||
print("SPUIColorExtension - Scan hex error")
|
||||
}
|
||||
self.init(red:red, green:green, blue:blue, alpha:alpha)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIFont {
|
||||
|
||||
public struct fonts {
|
||||
|
||||
public static func AvenirNext(type: BoldType, size: CGFloat) -> UIFont {
|
||||
return UIFont.createFont(.AvenirNext, boldType: type, size: size)
|
||||
}
|
||||
}
|
||||
|
||||
public static func system(type: BoldType, size: CGFloat) -> UIFont {
|
||||
if #available(iOS 8.2, *) {
|
||||
return UIFont.systemFont(ofSize: size, weight: self.getBoldTypeBy(boldType: type))
|
||||
} else {
|
||||
return self.createFont(.AvenirNext, boldType: type, size: size)
|
||||
}
|
||||
}
|
||||
|
||||
public static func createFont(_ fontType: FontType, boldType: BoldType, size: CGFloat) -> UIFont {
|
||||
return UIFont.init(
|
||||
name: self.getFontNameBy(fontType: fontType) + self.getBoldTypeNameBy(boldType: boldType),
|
||||
size: size
|
||||
)!
|
||||
}
|
||||
|
||||
private static func getFontNameBy(fontType: FontType) -> String {
|
||||
switch fontType {
|
||||
case .AvenirNext:
|
||||
return "AvenirNext"
|
||||
}
|
||||
}
|
||||
|
||||
private static func getBoldTypeNameBy(boldType: BoldType) -> String {
|
||||
switch boldType {
|
||||
case .UltraLight:
|
||||
return "-UltraLight"
|
||||
case .Light:
|
||||
return "-Light"
|
||||
case .Medium:
|
||||
return "-Medium"
|
||||
case .Regular:
|
||||
return "-Regular"
|
||||
case .Bold:
|
||||
return "-Bold"
|
||||
case .DemiBold:
|
||||
return "-DemiBold"
|
||||
default:
|
||||
return "-Regular"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@available(iOS 8.2, *)
|
||||
private static func getBoldTypeBy(boldType: BoldType) -> UIFont.Weight {
|
||||
switch boldType {
|
||||
case .UltraLight:
|
||||
return UIFont.Weight.ultraLight
|
||||
case .Light:
|
||||
return UIFont.Weight.light
|
||||
case .Medium:
|
||||
return UIFont.Weight.medium
|
||||
case .Regular:
|
||||
return UIFont.Weight.regular
|
||||
case .Bold:
|
||||
return UIFont.Weight.bold
|
||||
case .DemiBold:
|
||||
return UIFont.Weight.semibold
|
||||
case .Heavy:
|
||||
return UIFont.Weight.heavy
|
||||
default:
|
||||
return UIFont.Weight.regular
|
||||
}
|
||||
}
|
||||
|
||||
public enum FontType {
|
||||
case AvenirNext
|
||||
}
|
||||
|
||||
public enum BoldType {
|
||||
case Regular
|
||||
case Medium
|
||||
case Light
|
||||
case UltraLight
|
||||
case Heavy
|
||||
case Bold
|
||||
case DemiBold
|
||||
case None
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIImage {
|
||||
|
||||
public func resize(width: CGFloat) -> UIImage {
|
||||
let scale = width / self.size.width
|
||||
let newHeight = self.size.height * scale
|
||||
UIGraphicsBeginImageContext(CGSize(width: width, height: newHeight))
|
||||
self.draw(in: CGRect(x: 0, y: 0, width: width, height: newHeight))
|
||||
let newImage = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
return newImage!
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIImageView {
|
||||
|
||||
public func setNative() {
|
||||
self.layer.borderWidth = 0.5
|
||||
self.layer.borderColor = SPNativeStyleKit.Colors.midGray.cgColor
|
||||
self.layer.masksToBounds = true
|
||||
}
|
||||
|
||||
public func downloadedFrom(url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit, withComplection complection: @escaping (UIImage?) -> () = {_ in }) {
|
||||
DispatchQueue.main.async {
|
||||
self.contentMode = mode
|
||||
}
|
||||
URLSession.shared.dataTask(with: url) { (data, response, error) in
|
||||
guard
|
||||
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
|
||||
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
|
||||
let data = data, error == nil,
|
||||
let image = UIImage(data: data)
|
||||
else { complection(nil); return }
|
||||
DispatchQueue.main.async() { () -> Void in
|
||||
self.image = image
|
||||
complection(image)
|
||||
}
|
||||
}.resume()
|
||||
}
|
||||
|
||||
public func downloadedFrom(link: String, contentMode mode: UIView.ContentMode = .scaleAspectFit, withComplection complection: @escaping (UIImage?) -> () = {_ in }) {
|
||||
guard let url = URL(string: link) else { return }
|
||||
downloadedFrom(url: url, contentMode: mode, withComplection: complection)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UILabel {
|
||||
|
||||
func setShadowOffsetForLetters(blurRadius: CGFloat = 0, widthOffset: CGFloat = 0, heightOffset: CGFloat, opacity: CGFloat) {
|
||||
self.layer.shadowRadius = blurRadius
|
||||
self.layer.shadowOffset = CGSize(
|
||||
width: widthOffset,
|
||||
height: heightOffset
|
||||
)
|
||||
self.layer.shadowOpacity = Float(opacity)
|
||||
}
|
||||
|
||||
func setShadowOffsetFactorForLetters(blurRadius: CGFloat = 0, widthOffsetFactor: CGFloat = 0, heightOffsetFactor: CGFloat, opacity: CGFloat) {
|
||||
let widthOffset = widthOffsetFactor * self.frame.width
|
||||
let heightOffset = heightOffsetFactor * self.frame.height
|
||||
self.setShadowOffsetForLetters(blurRadius: blurRadius, widthOffset: widthOffset, heightOffset: heightOffset, opacity: opacity)
|
||||
}
|
||||
|
||||
func removeShadowForLetters() {
|
||||
self.setShadowOffsetForLetters(blurRadius: 0, widthOffset: 0, heightOffset: 0, opacity: 0)
|
||||
}
|
||||
|
||||
func setCenteringAlignment() {
|
||||
self.textAlignment = .center
|
||||
self.baselineAdjustment = .alignCenters
|
||||
}
|
||||
|
||||
func setLettersSpacing(_ value: CGFloat) {
|
||||
if let textString = text {
|
||||
let attrs: [NSAttributedString.Key : Any] = [.kern: value]
|
||||
attributedText = NSAttributedString(string: textString, attributes: attrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UINavigationController {
|
||||
|
||||
static var elementsColor: UIColor {
|
||||
get {
|
||||
if UINavigationBar.appearance().tintColor != nil {
|
||||
return UINavigationBar.appearance().tintColor
|
||||
} else {
|
||||
return SPNativeStyleKit.Colors.blue
|
||||
}
|
||||
}
|
||||
set {
|
||||
UINavigationBar.appearance().tintColor = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UITabBarController {
|
||||
|
||||
func addTabBarItem(title: String, image: UIImage, selectedImage: UIImage? = nil, controller: UIViewController) {
|
||||
|
||||
let tabBarItem = UITabBarItem(
|
||||
title: title,
|
||||
image: image,
|
||||
selectedImage: selectedImage ?? image
|
||||
)
|
||||
|
||||
controller.tabBarItem = tabBarItem
|
||||
|
||||
if self.viewControllers == nil { self.viewControllers = [controller] }
|
||||
else { self.viewControllers?.append(controller) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UITableView {
|
||||
|
||||
var isEmpty: Bool {
|
||||
return self.lastSectionWithRows == nil
|
||||
}
|
||||
|
||||
func isEmpty(section: Int) -> Bool {
|
||||
return self.numberOfRows(inSection: section) == 0
|
||||
}
|
||||
|
||||
var lastSection: Int {
|
||||
return self.numberOfSections - 1
|
||||
}
|
||||
|
||||
var lastSectionWithRows: Int? {
|
||||
if self.numberOfSections == 0 { return nil }
|
||||
var section = self.numberOfSections - 1
|
||||
if section < 0 { return nil }
|
||||
while section >= 0 {
|
||||
if self.numberOfRows(inSection: section) != 0 { return section }
|
||||
section -= 1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var firstSectionWithRows: Int? {
|
||||
if self.numberOfSections == 0 { return nil }
|
||||
var section = 0
|
||||
if section > self.numberOfSections - 1 { return nil }
|
||||
while section <= (self.numberOfSections - 1) {
|
||||
if self.numberOfRows(inSection: section) != 0 { return section }
|
||||
section += 1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UITableViewCell {
|
||||
|
||||
public var accessoryView: UIView? {
|
||||
return subviews.compactMap { $0 as? UIButton }.first
|
||||
}
|
||||
|
||||
var highlightedColor: UIColor? {
|
||||
get {
|
||||
return self.backgroundView?.backgroundColor
|
||||
}
|
||||
set {
|
||||
let backgroundView = UIView()
|
||||
backgroundView.backgroundColor = SPNativeStyleKit.Colors.customGray
|
||||
self.selectedBackgroundView = backgroundView
|
||||
}
|
||||
}
|
||||
|
||||
public func highlight() {
|
||||
self.setHighlighted(true, animated: false)
|
||||
self.setHighlighted(false, animated: true)
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UITextField {
|
||||
|
||||
var isEmptyText: Bool {
|
||||
get {
|
||||
if self.text == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
if self.text == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
import Photos
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
func present(_ viewControllerToPresent: UIViewController, completion: (() -> Swift.Void)? = nil) {
|
||||
self.present(viewControllerToPresent, animated: true, completion: completion)
|
||||
}
|
||||
|
||||
@objc func dismiss() {
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func wrapToNavigationController(statusBar: SPStatusBar = .dark) -> UINavigationController {
|
||||
let controller = SPStatusBarManagerNavigationController(rootViewController: self)
|
||||
controller.statusBar = statusBar
|
||||
return controller
|
||||
}
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
func dismissKeyboardWhenTappedAround() {
|
||||
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
|
||||
tap.cancelsTouchesInView = false
|
||||
view.addGestureRecognizer(tap)
|
||||
}
|
||||
|
||||
@objc func dismissKeyboard() {
|
||||
view.endEditing(true)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
func save(image: UIImage) {
|
||||
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized {
|
||||
UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.image(_:didFinishSavingWithError:contextInfo:)), nil)
|
||||
} else {
|
||||
print("Saving image error. Not allowed permission")
|
||||
}
|
||||
}
|
||||
|
||||
func saveVideo(url: String, complection: @escaping (Bool)->()) {
|
||||
DispatchQueue.global(qos: .utility).async {
|
||||
let urls = URL(string: url)
|
||||
let urldata = try? Data(contentsOf: urls!)
|
||||
if urldata != nil {
|
||||
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
|
||||
let filepath = "\(documentsPath)/tempfile.mp4"
|
||||
DispatchQueue.main.async {
|
||||
let urlsave = URL(fileURLWithPath: filepath)
|
||||
do {
|
||||
try urldata!.write(to: urlsave, options: Data.WritingOptions.atomic)
|
||||
PHPhotoLibrary.shared().performChanges({
|
||||
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: urlsave)
|
||||
}, completionHandler: { (status, error) in
|
||||
try? FileManager.default.removeItem(atPath: filepath)
|
||||
DispatchQueue.main.async {
|
||||
complection(error == nil)
|
||||
}
|
||||
})
|
||||
} catch {
|
||||
try? FileManager.default.removeItem(atPath: filepath)
|
||||
complection(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
|
||||
if let _ = error {
|
||||
self.imageSaved(isSuccses: false)
|
||||
} else {
|
||||
self.imageSaved(isSuccses: true)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func imageSaved(isSuccses: Bool) {
|
||||
fatalError("SPUIViewControllerExtenshion - Need ovveride 'imageSaved' func")
|
||||
}
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
func setPrefersLargeNavigationTitle(_ title: String, smallScreenToSmallBar: Bool = true) {
|
||||
self.navigationItem.title = title
|
||||
if #available(iOS 11.0, *) {
|
||||
self.navigationItem.largeTitleDisplayMode = .automatic
|
||||
self.navigationController?.navigationBar.prefersLargeTitles = true
|
||||
}
|
||||
|
||||
if smallScreenToSmallBar {
|
||||
if self.view.frame.width < 375 {
|
||||
self.setNavigationTitle(title, style: .small)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setNavigationTitle(_ title: String, style: SPNavigationTitleStyle) {
|
||||
self.navigationItem.title = title
|
||||
switch style {
|
||||
case .large:
|
||||
if #available(iOS 11.0, *) {
|
||||
self.navigationController?.navigationBar.prefersLargeTitles = true
|
||||
self.navigationItem.largeTitleDisplayMode = .always
|
||||
}
|
||||
case .small:
|
||||
if #available(iOS 11.0, *) {
|
||||
self.navigationItem.largeTitleDisplayMode = .never
|
||||
}
|
||||
case .stork:
|
||||
if #available(iOS 11.0, *) {
|
||||
self.navigationItem.largeTitleDisplayMode = .never
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
var safeArea: UIEdgeInsets {
|
||||
if #available(iOS 11.0, *) {
|
||||
return self.view.safeAreaInsets
|
||||
} else {
|
||||
return UIEdgeInsets.zero
|
||||
}
|
||||
}
|
||||
|
||||
var navigationBarHeight: CGFloat {
|
||||
return self.navigationController?.navigationBar.frame.height ?? 0
|
||||
}
|
||||
|
||||
static var statusBarHeight: CGFloat {
|
||||
return UIApplication.shared.statusBarFrame.height
|
||||
}
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
var navigationTitleColor: UIColor? {
|
||||
get {
|
||||
return (self.navigationController?.navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor) ?? nil
|
||||
}
|
||||
set {
|
||||
let textAttributes: [NSAttributedString.Key: Any]? = [NSAttributedString.Key.foregroundColor: newValue ?? UIColor.black]
|
||||
self.navigationController?.navigationBar.titleTextAttributes = textAttributes
|
||||
if #available(iOS 11.0, *) {
|
||||
self.navigationController?.navigationBar.largeTitleTextAttributes = textAttributes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIView {
|
||||
|
||||
var viewController: UIViewController? {
|
||||
get {
|
||||
if let nextResponder = self.next as? UIViewController { return nextResponder }
|
||||
else if let nextResponder = self.next as? UIView { return nextResponder.viewController }
|
||||
else { return nil }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension UIView {
|
||||
|
||||
var topSafeArea: CGFloat {
|
||||
var topSafeArea: CGFloat = 0
|
||||
if #available(iOS 11.0, *) {
|
||||
topSafeArea = self.safeAreaInsets.top
|
||||
}
|
||||
return topSafeArea
|
||||
}
|
||||
|
||||
var bottomSafeArea: CGFloat {
|
||||
var bottomSafeArea: CGFloat = 0
|
||||
if #available(iOS 11.0, *) {
|
||||
bottomSafeArea = self.safeAreaInsets.bottom
|
||||
}
|
||||
return bottomSafeArea
|
||||
}
|
||||
|
||||
func setHeight(_ height: CGFloat) {
|
||||
self.frame = CGRect.init(x: self.frame.origin.x, y: self.frame.origin.y, width: self.frame.width, height: height)
|
||||
}
|
||||
|
||||
func setWidth(_ width: CGFloat) {
|
||||
self.frame = CGRect.init(x: self.frame.origin.x, y: self.frame.origin.y, width: width, height: self.frame.height)
|
||||
}
|
||||
|
||||
func setEqualsFrameFromBounds(_ view: UIView, withWidthFactor widthFactor: CGFloat = 1, maxWidth: CGFloat? = nil, withHeightFactor heightFactor: CGFloat = 1, maxHeight: CGFloat? = nil, withCentering: Bool = false) {
|
||||
self.setEqualsFrameFromBounds(view.bounds, withWidthFactor: widthFactor, maxWidth: maxWidth, withHeightFactor: heightFactor, maxHeight: maxHeight, withCentering: withCentering)
|
||||
}
|
||||
|
||||
func setEqualsFrameFromBounds(_ bounds: CGRect, withWidthFactor widthFactor: CGFloat = 1, maxWidth: CGFloat? = nil, withHeightFactor heightFactor: CGFloat = 1, maxHeight: CGFloat? = nil, withCentering: Bool = false) {
|
||||
|
||||
var width = bounds.width * widthFactor
|
||||
if maxWidth != nil { width.setIfMore(when: maxWidth!) }
|
||||
|
||||
var height = bounds.height * heightFactor
|
||||
if maxHeight != nil { height.setIfMore(when: maxHeight!) }
|
||||
|
||||
self.frame = CGRect.init(x: 0, y: 0, width: width, height: height)
|
||||
|
||||
if withCentering {
|
||||
self.center.x = bounds.width / 2
|
||||
self.center.y = bounds.height / 2
|
||||
}
|
||||
}
|
||||
|
||||
func setEqualsBoundsFromSuperview(customWidth: CGFloat? = nil, customHeight: CGFloat? = nil) {
|
||||
if self.superview == nil { return }
|
||||
self.frame = CGRect.init(origin: CGPoint.zero, size: self.superview!.frame.size)
|
||||
if customWidth != nil {
|
||||
self.frame = CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: customWidth!, height: self.frame.height))
|
||||
}
|
||||
if customHeight != nil {
|
||||
self.frame = CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: self.frame.width, height: customHeight!))
|
||||
}
|
||||
}
|
||||
|
||||
func resize(width: CGFloat) {
|
||||
let relativeFactor = self.frame.width / self.frame.height
|
||||
if relativeFactor.isNaN { return }
|
||||
self.frame = CGRect.init(
|
||||
x: self.frame.origin.x,
|
||||
y: self.frame.origin.y,
|
||||
width: width,
|
||||
height: width * relativeFactor
|
||||
)
|
||||
}
|
||||
|
||||
func resize(height: CGFloat) {
|
||||
let relativeFactor = self.frame.width / self.frame.height
|
||||
if relativeFactor.isNaN { return }
|
||||
self.frame = CGRect.init(
|
||||
x: self.frame.origin.x,
|
||||
y: self.frame.origin.y,
|
||||
width: height / relativeFactor,
|
||||
height: height
|
||||
)
|
||||
}
|
||||
|
||||
func setYCenteringFromSuperview() {
|
||||
self.center.y = (self.superview?.frame.height ?? 0) / 2
|
||||
}
|
||||
|
||||
func setXCenteringFromSuperview() {
|
||||
self.center.x = (self.superview?.frame.width ?? 0) / 2
|
||||
}
|
||||
|
||||
func setToCenterInSuperview() {
|
||||
self.center = CGPoint.init(x: ((self.superview?.frame.width) ?? 0) / 2, y: ((self.superview?.frame.height) ?? 0) / 2)
|
||||
}
|
||||
}
|
||||
|
||||
public extension UIView {
|
||||
|
||||
func setParalax(amountFactor: CGFloat) {
|
||||
let amount = self.frame.minSideSize * amountFactor
|
||||
self.setParalax(amount: amount)
|
||||
}
|
||||
|
||||
func setParalax(amount: CGFloat) {
|
||||
self.motionEffects.removeAll()
|
||||
let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis)
|
||||
horizontal.minimumRelativeValue = -amount
|
||||
horizontal.maximumRelativeValue = amount
|
||||
|
||||
let vertical = UIInterpolatingMotionEffect(keyPath: "center.y", type: .tiltAlongVerticalAxis)
|
||||
vertical.minimumRelativeValue = -amount
|
||||
vertical.maximumRelativeValue = amount
|
||||
|
||||
let group = UIMotionEffectGroup()
|
||||
group.motionEffects = [horizontal, vertical]
|
||||
self.addMotionEffect(group)
|
||||
}
|
||||
}
|
||||
|
||||
public extension UIView {
|
||||
|
||||
func addGrade(alpha: CGFloat, color: UIColor = UIColor.black) -> UIView {
|
||||
let gradeView = UIView.init()
|
||||
gradeView.alpha = 0
|
||||
self.addSubview(gradeView)
|
||||
SPConstraints.setEqualSize(gradeView, superVuew: self)
|
||||
gradeView.alpha = alpha
|
||||
gradeView.backgroundColor = color
|
||||
return gradeView
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
|
||||
func setShadow(
|
||||
xTranslationFactor: CGFloat,
|
||||
yTranslationFactor: CGFloat,
|
||||
widthRelativeFactor: CGFloat,
|
||||
heightRelativeFactor: CGFloat,
|
||||
blurRadiusFactor: CGFloat,
|
||||
shadowOpacity: CGFloat,
|
||||
cornerRadiusFactor: CGFloat = 0
|
||||
) {
|
||||
let shadowWidth = self.frame.width * widthRelativeFactor
|
||||
let shadowHeight = self.frame.height * heightRelativeFactor
|
||||
|
||||
let xTranslation = (self.frame.width - shadowWidth) / 2 + (self.frame.width * xTranslationFactor)
|
||||
let yTranslation = (self.frame.height - shadowHeight) / 2 + (self.frame.height * yTranslationFactor)
|
||||
|
||||
let cornerRadius = self.frame.minSideSize * cornerRadiusFactor
|
||||
|
||||
let shadowPath = UIBezierPath.init(
|
||||
roundedRect: CGRect.init(x: xTranslation, y: yTranslation, width: shadowWidth, height: shadowHeight),
|
||||
cornerRadius: cornerRadius
|
||||
)
|
||||
|
||||
let blurRadius = self.frame.minSideSize * blurRadiusFactor
|
||||
|
||||
self.layer.shadowColor = UIColor.black.cgColor
|
||||
self.layer.shadowOffset = CGSize.zero
|
||||
self.layer.shadowOpacity = Float(shadowOpacity)
|
||||
self.layer.shadowRadius = blurRadius
|
||||
self.layer.masksToBounds = false
|
||||
self.layer.shadowPath = shadowPath.cgPath;
|
||||
}
|
||||
|
||||
func setShadow(
|
||||
xTranslation: CGFloat,
|
||||
yTranslation: CGFloat,
|
||||
widthRelativeFactor: CGFloat,
|
||||
heightRelativeFactor: CGFloat,
|
||||
blurRadius: CGFloat,
|
||||
shadowOpacity: CGFloat,
|
||||
cornerRadius: CGFloat = 0
|
||||
) {
|
||||
let shadowWidth = self.frame.width * widthRelativeFactor
|
||||
let shadowHeight = self.frame.height * heightRelativeFactor
|
||||
|
||||
let shadowPath = UIBezierPath.init(
|
||||
roundedRect: CGRect.init(x: xTranslation, y: yTranslation, width: shadowWidth, height: shadowHeight),
|
||||
cornerRadius: cornerRadius
|
||||
)
|
||||
|
||||
self.layer.shadowColor = UIColor.black.cgColor
|
||||
self.layer.shadowOffset = CGSize.zero
|
||||
self.layer.shadowOpacity = Float(shadowOpacity)
|
||||
self.layer.shadowRadius = blurRadius
|
||||
self.layer.masksToBounds = false
|
||||
self.layer.shadowPath = shadowPath.cgPath
|
||||
}
|
||||
|
||||
func removeShadow() {
|
||||
self.layer.shadowColor = nil
|
||||
self.layer.shadowOffset = CGSize.zero
|
||||
self.layer.shadowOpacity = 0
|
||||
self.layer.shadowPath = nil
|
||||
}
|
||||
|
||||
func addShadowOpacityAnimation(to: CGFloat, duration: CFTimeInterval) {
|
||||
let animation = CABasicAnimation(keyPath:"shadowOpacity")
|
||||
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
|
||||
animation.fromValue = self.layer.cornerRadius
|
||||
animation.fromValue = self.layer.shadowOpacity
|
||||
animation.toValue = to
|
||||
animation.duration = duration
|
||||
self.layer.add(animation, forKey: "shadowOpacity")
|
||||
self.layer.shadowOpacity = Float(to)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
|
||||
func addCornerRadiusAnimation(to: CGFloat, duration: CFTimeInterval) {
|
||||
let animation = CABasicAnimation(keyPath:"cornerRadius")
|
||||
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
|
||||
animation.fromValue = self.layer.cornerRadius
|
||||
animation.toValue = to
|
||||
animation.duration = duration
|
||||
self.layer.add(animation, forKey: "cornerRadius")
|
||||
self.layer.cornerRadius = to
|
||||
}
|
||||
|
||||
func show(duration: TimeInterval = 0.3) {
|
||||
self.isHidden = false
|
||||
SPAnimation.animate(duration, animations: {
|
||||
self.alpha = 1
|
||||
})
|
||||
}
|
||||
|
||||
func hide(duration: TimeInterval = 0.3) {
|
||||
SPAnimation.animate(duration, animations: {
|
||||
self.alpha = 0
|
||||
}, withComplection: {
|
||||
self.isHidden = true
|
||||
})
|
||||
}
|
||||
|
||||
func removeAllAnimations() {
|
||||
self.layer.removeAllAnimations()
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
|
||||
func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
|
||||
let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
|
||||
let mask = CAShapeLayer()
|
||||
mask.path = path.cgPath
|
||||
self.layer.mask = mask
|
||||
}
|
||||
|
||||
func round() {
|
||||
self.layer.cornerRadius = self.frame.minSideSize / 2
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIVisualEffectView {
|
||||
|
||||
convenience init(style: UIBlurEffect.Style) {
|
||||
let effect = UIBlurEffect(style: style)
|
||||
self.init(effect: effect)
|
||||
}
|
||||
|
||||
convenience init(vibrancy style: UIBlurEffect.Style) {
|
||||
let effect = UIBlurEffect(style: style)
|
||||
let vibrancyEffect = UIVibrancyEffect(blurEffect: effect)
|
||||
self.init(effect: vibrancyEffect)
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
|
||||
extension UserDefaults {
|
||||
|
||||
func set(stringArray array: [String], forKey key: String) {
|
||||
self.set(array, forKey: key)
|
||||
}
|
||||
|
||||
func set(boolArray array: [Bool], forKey key: String) {
|
||||
self.set(array, forKey: key)
|
||||
}
|
||||
|
||||
func boolArray(forKey defaultName: String) -> [Bool] {
|
||||
return UserDefaults.standard.array(forKey: defaultName) as? [Bool] ?? []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
struct SPLaunch {
|
||||
|
||||
static func run() {
|
||||
self.count += 1
|
||||
}
|
||||
|
||||
static var count: Int {
|
||||
get {
|
||||
return UserDefaults.standard.value(forKey: "SPLaunchCount") as? Int ?? 0
|
||||
}
|
||||
set {
|
||||
UserDefaults.standard.set(newValue, forKey: "SPLaunchCount")
|
||||
}
|
||||
}
|
||||
|
||||
static var isFirstOpen: Bool {
|
||||
return (self.count == 1) || (self.count == 0)
|
||||
}
|
||||
|
||||
|
||||
private init() {}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
struct SPLayout {
|
||||
|
||||
static func sizeWith(widthFactor: CGFloat, maxWidth: CGFloat, heightFactor: CGFloat, maxHeight: CGFloat, relativeSideFactor: CGFloat, from relativeSize: CGSize) -> CGSize {
|
||||
|
||||
var widthArea = relativeSize.width * widthFactor
|
||||
var heightArea = relativeSize.height * heightFactor
|
||||
|
||||
widthArea.setIfMore(when: maxWidth)
|
||||
heightArea.setIfMore(when: maxHeight)
|
||||
|
||||
var prepareWidth = widthArea
|
||||
var prepareHeight = widthArea / relativeSideFactor
|
||||
if prepareHeight > heightArea {
|
||||
prepareHeight = heightArea
|
||||
prepareWidth = heightArea * relativeSideFactor
|
||||
}
|
||||
return CGSize.init(width: prepareWidth, height: prepareHeight)
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
enum SPLocale: String, CaseIterable {
|
||||
|
||||
case ru = "ru"
|
||||
case en = "en"
|
||||
|
||||
static var current: SPLocale {
|
||||
set {
|
||||
UserDefaults.standard.set([newValue.languageCode], forKey: "AppleLanguages")
|
||||
}
|
||||
get {
|
||||
let locale = Locale.preferredLanguages[0].components(separatedBy: "-")[0]
|
||||
|
||||
switch locale {
|
||||
case "en":
|
||||
return .en
|
||||
case "ru":
|
||||
return .ru
|
||||
default:
|
||||
return .ru
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var languageCode: String {
|
||||
return self.rawValue
|
||||
}
|
||||
|
||||
var describtion: String {
|
||||
switch self {
|
||||
case .en:
|
||||
return "English"
|
||||
case .ru:
|
||||
return "Русский"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
import MessageUI
|
||||
|
||||
struct SPMail {
|
||||
|
||||
static var canSendEmail: Bool {
|
||||
return MFMailComposeViewController.canSendMail()
|
||||
}
|
||||
|
||||
static func openMailApp(to email: String, subject: String? = nil, body: String? = nil) {
|
||||
let parametrs = "mailto:\(email)?subject=\(subject ?? "")&body=\(body ?? "")".addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
|
||||
|
||||
if parametrs != nil {
|
||||
if let url: URL = URL(string: parametrs!) {
|
||||
if UIApplication.shared.canOpenURL(url) {
|
||||
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func mailDialog(to email: String, subject: String? = nil, body: String? = nil, on viewController: UIViewController) {
|
||||
let mailVC = MFMailComposeViewController()
|
||||
mailVC.mailComposeDelegate = SPMailSingltone.sharedInstance
|
||||
|
||||
mailVC.setToRecipients([email])
|
||||
|
||||
if subject != nil {
|
||||
mailVC.setSubject(subject!)
|
||||
}
|
||||
|
||||
if body != nil {
|
||||
mailVC.setMessageBody(body!, isHTML: false)
|
||||
}
|
||||
|
||||
viewController.present(mailVC, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
fileprivate final class SPMailSingltone: NSObject, MFMailComposeViewControllerDelegate {
|
||||
|
||||
static let sharedInstance = SPMailSingltone()
|
||||
|
||||
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
||||
controller.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
// Helper function inserted by Swift 4.2 migrator.
|
||||
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
|
||||
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
import UserNotifications
|
||||
|
||||
struct SPLocalNotification {
|
||||
|
||||
static func add(from timeInterval: TimeInterval, body: String, title: String? = nil, identifier: String? = nil) {
|
||||
|
||||
let content = UNMutableNotificationContent()
|
||||
content.body = body
|
||||
content.title = title ?? ""
|
||||
content.badge = NSNumber(value: SPBadge.number + 1)
|
||||
content.sound = UNNotificationSound.default
|
||||
|
||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: false)
|
||||
|
||||
let identifier = identifier ?? "\(timeInterval)\(body)\(Int.random(min: 0, max: 1000))"
|
||||
|
||||
let notification = UNNotificationRequest(
|
||||
identifier: identifier,
|
||||
content: content,
|
||||
trigger: trigger
|
||||
)
|
||||
|
||||
let center = UNUserNotificationCenter.current()
|
||||
center.add(notification) { (error) in
|
||||
if let error = error {
|
||||
print("SPLocalNotification - \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func add(in date: Date, body: String, title: String? = nil, identifier: String? = nil) {
|
||||
|
||||
let content = UNMutableNotificationContent()
|
||||
content.body = body
|
||||
content.title = title ?? ""
|
||||
content.badge = NSNumber(value: SPBadge.number + 1)
|
||||
content.sound = UNNotificationSound.default
|
||||
|
||||
let triggerDate = Calendar.current.dateComponents([.year,.month,.day,.hour,.minute,.second,], from: date)
|
||||
let trigger = UNCalendarNotificationTrigger.init(dateMatching: triggerDate, repeats: false)
|
||||
|
||||
let identifier = identifier ?? "\(date)\(body)\(Int.random(min: 0, max: 1000))"
|
||||
|
||||
let notification = UNNotificationRequest(
|
||||
identifier: identifier,
|
||||
content: content,
|
||||
trigger: trigger
|
||||
)
|
||||
|
||||
let center = UNUserNotificationCenter.current()
|
||||
center.add(notification) { (error) in
|
||||
if let error = error {
|
||||
print("SPLocalNotification - \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func remove(identifier: String) {
|
||||
let center = UNUserNotificationCenter.current()
|
||||
center.removePendingNotificationRequests(withIdentifiers: [identifier])
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
import SafariServices
|
||||
|
||||
struct SPOpener {
|
||||
|
||||
struct App {
|
||||
|
||||
static func system(app: SPSystemApp) {
|
||||
switch app {
|
||||
case SPSystemApp.photos:
|
||||
guard let settingsUrl = URL(string: "photos-redirect://") else {
|
||||
return
|
||||
}
|
||||
|
||||
if UIApplication.shared.canOpenURL(settingsUrl) {
|
||||
if #available(iOS 10.0, *) {
|
||||
UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
|
||||
print("SPOpener - Photos opened: \(success)")
|
||||
})
|
||||
} else {
|
||||
UIApplication.shared.openURL(settingsUrl as URL)
|
||||
}
|
||||
} else {
|
||||
print("SPOpener - Photos not opened")
|
||||
}
|
||||
case SPSystemApp.setting:
|
||||
DispatchQueue.main.async {
|
||||
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
|
||||
return
|
||||
}
|
||||
|
||||
if UIApplication.shared.canOpenURL(settingsUrl) {
|
||||
if #available(iOS 10.0, *) {
|
||||
UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
|
||||
print("SPOpener - Settings opened: \(success)")
|
||||
})
|
||||
} else {
|
||||
UIApplication.shared.openURL(settingsUrl as URL)
|
||||
}
|
||||
} else {
|
||||
print("SPOpener - Settings not opened")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
struct Link {
|
||||
|
||||
public static func redirectToBrowserAndOpen(link: String) {
|
||||
|
||||
guard let url = URL(string: link) else {
|
||||
print("SPOpener - can not create URL")
|
||||
return
|
||||
}
|
||||
|
||||
self.redirectToBrowserAndOpen(link: url)
|
||||
}
|
||||
|
||||
public static func redirectToBrowserAndOpen(link: URL) {
|
||||
if #available(iOS 10.0, *) {
|
||||
UIApplication.shared.open(link, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
|
||||
} else {
|
||||
UIApplication.shared.openURL(link)
|
||||
}
|
||||
}
|
||||
|
||||
public static func openInsideApp(link: String, on viewController: UIViewController) {
|
||||
if let url = URL.init(string: link) {
|
||||
let safariController = SFSafariViewController.init(url: url)
|
||||
viewController.present(safariController, animated: true, completion: nil)
|
||||
} else {
|
||||
print("SPOpener - openInsideApp - invalid link")
|
||||
}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Helper function inserted by Swift 4.2 migrator.
|
||||
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
|
||||
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension String {
|
||||
public static func random(count: Int) -> String {
|
||||
let strings = [
|
||||
"В доме кардинала от меня не было тайн; не раз видел я, как он усердно перелистывает старинные книги и жадно роется в пыли фамильных рукописей. Когда я как-то упрекнул его за бесполезные бессонные ночи, после которых он впадал в болезненное уныние, он взглянул на меня с горькой улыбкой и раскрыл передо мною историю города Рима. В этой книге, в двадцатой главе жизнеописания папы Александра Шестого, я прочел следующие строки, навсегда оставшиеся в моей памяти",
|
||||
"По этому поводу между отцом и сыном завязался спор. Цезарь считал, что достаточно применить одно из тех средств, которые он всегда держал наготове для своих ближайших друзей, а именно: пресловутый ключ, которым то одного, то другого просили отпереть некий шкаф. На ключе был крохотный железный шип – недосмотр слесаря. Каждый, кто трудился над тугим замком, накалывал себе палец и на другой день умирал. Был еще перстень с львиной головой, который Цезарь надевал, когда хотел пожать руку той или иной особе. Лев впивался в кожу этих избранных рук, и через сутки наступала смерть.",
|
||||
"Стол накрыли в папских виноградниках возле Сан-Пьетро-ин-Винколи, в прелестном уголке, понаслышке знакомом кардиналам. Роспильози, в восторге от своего нового звания и предвкушая пир, явился с самым веселым лицом. Спада, человек осторожный и очень любивший своего племянника, молодого офицера, подававшего блистательные надежды, взял лист бумаги, перо и написал свое завещание. Потом он послал сказать племяннику, чтобы тот ждал его у виноградников; но посланный, по-видимому, не застал того дома.",
|
||||
"Спада знал, что значит приглашение на обед. С тех пор как христианство – глубоко цивилизующая сила – восторжествовало в Риме, уже не центурион являлся объявить от имени тирана: «Цезарь желает, чтобы ты умер», а любезный легат с улыбкой говорил от имени папы: «Его святейшество желает, чтобы вы с ним отобедали»"
|
||||
]
|
||||
return String(strings.random()!.prefix(count))
|
||||
}
|
||||
}
|
||||
|
||||
public extension Int {
|
||||
public static func random(_ n: Int) -> Int {
|
||||
return Int(arc4random_uniform(UInt32(n)))
|
||||
}
|
||||
|
||||
public static func random(min: Int, max: Int) -> Int {
|
||||
return Int(arc4random_uniform(UInt32(max - min - 1))) + min
|
||||
}
|
||||
}
|
||||
|
||||
public extension Double {
|
||||
public static func random() -> Double {
|
||||
return Double(arc4random()) / 0xFFFFFFFF
|
||||
}
|
||||
|
||||
public static func random(min: Double, max: Double) -> Double {
|
||||
return Double.random() * (max - min) + min
|
||||
}
|
||||
}
|
||||
|
||||
public extension Float {
|
||||
public static func random() -> Float {
|
||||
return Float(arc4random()) / 0xFFFFFFFF
|
||||
}
|
||||
|
||||
public static func random(min: Float, max: Float) -> Float {
|
||||
return Float.random() * (max - min) + min
|
||||
}
|
||||
}
|
||||
|
||||
public extension CGFloat {
|
||||
public static func random() -> CGFloat {
|
||||
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
|
||||
}
|
||||
|
||||
public static func random(min: CGFloat, max: CGFloat) -> CGFloat {
|
||||
return CGFloat.random() * (max - min) + min
|
||||
}
|
||||
}
|
||||
|
||||
public extension Collection {
|
||||
/// Return a copy of `self` with its elements shuffled
|
||||
func shuffle() -> [Iterator.Element] {
|
||||
var list = Array(self)
|
||||
list.shuffleInPlace()
|
||||
return list
|
||||
}
|
||||
}
|
||||
|
||||
extension Collection where Index == Int {
|
||||
|
||||
func random() -> Iterator.Element? {
|
||||
return isEmpty ? nil : self[Int(arc4random_uniform(UInt32(endIndex)))]
|
||||
}
|
||||
}
|
||||
|
||||
public extension MutableCollection where Index == Int {
|
||||
/// Shuffle the elements of `self` in-place.
|
||||
mutating func shuffleInPlace() {
|
||||
// empty and single-element collections don't shuffle
|
||||
if count < 2 { return }
|
||||
|
||||
for i in startIndex ..< endIndex - 1 {
|
||||
let j = Int(arc4random_uniform(UInt32(endIndex - i))) + i
|
||||
guard i != j else { continue }
|
||||
self.swapAt(i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
|
||||
struct SPShadow { private init() {} }
|
||||
@@ -0,0 +1,98 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension SPShadow {
|
||||
|
||||
struct DeepStyle {
|
||||
|
||||
private init() {}
|
||||
|
||||
public static func setFor(label: UILabel) {
|
||||
var offset = label.frame.height * 0.03
|
||||
offset.setIfMore(when: 1)
|
||||
label.setShadowOffsetForLetters(heightOffset: offset, opacity: 0.35)
|
||||
}
|
||||
|
||||
public static func setFor(view: UIView) {
|
||||
|
||||
let xTranslationFactor: CGFloat = 0
|
||||
let yTranslationFactor: CGFloat = 0.18
|
||||
var widthRelativeFactor: CGFloat = 0.68
|
||||
let heightRelativeFactor: CGFloat = 0.78
|
||||
let blurRadiusFactor: CGFloat = 0.1
|
||||
let shadowOpacity: CGFloat = 0.3
|
||||
|
||||
if view.frame.width < 120 {
|
||||
widthRelativeFactor = widthRelativeFactor * 0.8
|
||||
}
|
||||
|
||||
var shadowWidth = view.frame.width * widthRelativeFactor
|
||||
if (view.frame.width - shadowWidth) / 2 > 35 {
|
||||
shadowWidth = view.frame.width - 70
|
||||
widthRelativeFactor = shadowWidth / view.frame.width
|
||||
}
|
||||
let shadowHeight = view.frame.height * heightRelativeFactor
|
||||
|
||||
let xTranslation = (view.frame.width - shadowWidth) / 2 + (view.frame.width * xTranslationFactor)
|
||||
var yTranslation = (view.frame.height - shadowHeight) / 2 + (view.frame.height * yTranslationFactor)
|
||||
|
||||
let minBottomSpace: CGFloat = 6
|
||||
if (yTranslation + shadowHeight - view.frame.height) < minBottomSpace {
|
||||
yTranslation = view.frame.height + minBottomSpace - shadowHeight
|
||||
}
|
||||
|
||||
let maxBottomSpace: CGFloat = 12
|
||||
if (yTranslation + shadowHeight - view.frame.height) > maxBottomSpace {
|
||||
yTranslation = view.frame.height + maxBottomSpace - shadowHeight
|
||||
}
|
||||
|
||||
var blurRadius = view.frame.minSideSize * blurRadiusFactor
|
||||
blurRadius.setIfMore(when: 10)
|
||||
blurRadius.setIfFewer(when: 7)
|
||||
|
||||
view.setShadow(
|
||||
xTranslation: xTranslation,
|
||||
yTranslation: yTranslation,
|
||||
widthRelativeFactor: widthRelativeFactor,
|
||||
heightRelativeFactor: heightRelativeFactor,
|
||||
blurRadius: blurRadius,
|
||||
shadowOpacity: shadowOpacity,
|
||||
cornerRadius: view.layer.cornerRadius
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
|
||||
func setDeepShadow() {
|
||||
SPShadow.DeepStyle.setFor(view: self)
|
||||
}
|
||||
}
|
||||
|
||||
extension UILabel {
|
||||
|
||||
func setDeepShadowForLetters() {
|
||||
SPShadow.DeepStyle.setFor(label: self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public struct SPShare {
|
||||
|
||||
public struct Native {
|
||||
|
||||
static func share(text: String? = nil, fileNames: [String] = [], images: [UIImage] = [], complection: ((_ isSharing: Bool)->())? = nil, sourceView: UIView, on viewController: UIViewController) {
|
||||
|
||||
var shareData: [Any] = []
|
||||
if text != nil {
|
||||
shareData.append(text!)
|
||||
}
|
||||
|
||||
for file in fileNames {
|
||||
let path = Bundle.main.path(forResource: file, ofType: "")
|
||||
if path != nil {
|
||||
let fileData = URL.init(fileURLWithPath: path!)
|
||||
shareData.append(fileData)
|
||||
}
|
||||
}
|
||||
|
||||
for image in images {
|
||||
shareData.append(image)
|
||||
}
|
||||
|
||||
let shareViewController = UIActivityViewController(activityItems: shareData, applicationActivities: nil)
|
||||
shareViewController.completionWithItemsHandler = {(activityType: UIActivity.ActivityType?, completed: Bool, returnedItems: [Any]?, error: Error?) in
|
||||
if !completed {
|
||||
complection?(false)
|
||||
return
|
||||
}
|
||||
complection?(true)
|
||||
}
|
||||
shareViewController.modalPresentationStyle = .popover
|
||||
shareViewController.popoverPresentationController?.sourceView = sourceView
|
||||
shareViewController.popoverPresentationController?.sourceRect = sourceView.bounds
|
||||
|
||||
viewController.present(shareViewController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPInstagram {
|
||||
|
||||
static var isSetApp: Bool {
|
||||
if UIApplication.shared.canOpenURL(URL(string: "instagram://user?username=test")!) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
static func openPost(id: String) {
|
||||
let instagramHooks = "instagram://media?id=\(id)"
|
||||
let instagramUrl = URL(string: instagramHooks)
|
||||
let safariURL = URL(string: "instagram.com/\(id)")!
|
||||
if UIApplication.shared.canOpenURL(instagramUrl!) {
|
||||
UIApplication.shared.open(instagramUrl!, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
|
||||
} else {
|
||||
SPOpener.Link.redirectToBrowserAndOpen(link: safariURL)
|
||||
}
|
||||
}
|
||||
|
||||
static func openUser(username: String) {
|
||||
let instagramHooks = "instagram://user?username=\(username)"
|
||||
let instagramUrl = URL(string: instagramHooks)
|
||||
let safariURL = URL(string: "https://instagram.com/\(username)")!
|
||||
if UIApplication.shared.canOpenURL(instagramUrl!) {
|
||||
UIApplication.shared.open(instagramUrl!, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
|
||||
} else {
|
||||
SPOpener.Link.redirectToBrowserAndOpen(link: safariURL)
|
||||
}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
// Helper function inserted by Swift 4.2 migrator.
|
||||
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
|
||||
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPTelegram {
|
||||
|
||||
static var isSetApp: Bool {
|
||||
if UIApplication.shared.canOpenURL(URL(string: "tg://msg?text=test")!) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
static func share(text: String, complection: @escaping (_ isOpened: Bool)->() = {_ in }) {
|
||||
let urlStringEncoded = text.addingPercentEncoding( withAllowedCharacters: .urlHostAllowed)
|
||||
let urlOptional = URL(string: "tg://msg?text=\(urlStringEncoded ?? "")")
|
||||
if let url = urlOptional {
|
||||
if UIApplication.shared.canOpenURL(url) {
|
||||
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: complection)
|
||||
} else {
|
||||
complection(false)
|
||||
}
|
||||
} else {
|
||||
complection(false)
|
||||
}
|
||||
}
|
||||
|
||||
static func joinChannel(id: String) {
|
||||
let url = "https://t.me/joinchat/\(id)"
|
||||
SPOpener.Link.redirectToBrowserAndOpen(link: url)
|
||||
}
|
||||
|
||||
static func openBot(username: String) {
|
||||
var username = username
|
||||
if username.first == "@" {
|
||||
username.removeFirst()
|
||||
}
|
||||
let url = "https://telegram.me/\(username)"
|
||||
SPOpener.Link.redirectToBrowserAndOpen(link: url)
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
// Helper function inserted by Swift 4.2 migrator.
|
||||
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
|
||||
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPTwitter {
|
||||
|
||||
static var isSetApp: Bool {
|
||||
if UIApplication.shared.canOpenURL(URL(string: "twitter://post?message=test")!) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
static func share(text: String, complection: @escaping (_ isOpened: Bool)->() = {_ in }) {
|
||||
let urlStringEncoded = text.addingPercentEncoding( withAllowedCharacters: .urlHostAllowed)
|
||||
let urlOptional = URL(string: "twitter://post?message=\(urlStringEncoded ?? "")")
|
||||
if let url = urlOptional {
|
||||
if UIApplication.shared.canOpenURL(url) {
|
||||
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: complection)
|
||||
} else {
|
||||
complection(false)
|
||||
}
|
||||
} else {
|
||||
complection(false)
|
||||
}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
// Helper function inserted by Swift 4.2 migrator.
|
||||
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
|
||||
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPViber {
|
||||
|
||||
static var isSetApp: Bool {
|
||||
if UIApplication.shared.canOpenURL(URL(string: "viber://forward?text=test")!) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
static func share(text: String, complection: @escaping (_ isOpened: Bool)->() = {_ in }) {
|
||||
let urlStringEncoded = text.addingPercentEncoding( withAllowedCharacters: .urlHostAllowed)
|
||||
let urlOptional = URL(string: "viber://forward?text=\(urlStringEncoded ?? "")")
|
||||
if let url = urlOptional {
|
||||
if UIApplication.shared.canOpenURL(url) {
|
||||
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: complection)
|
||||
} else {
|
||||
complection(false)
|
||||
}
|
||||
} else {
|
||||
complection(false)
|
||||
}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Helper function inserted by Swift 4.2 migrator.
|
||||
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
|
||||
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPWhatsApp {
|
||||
|
||||
static var isSetApp: Bool {
|
||||
if UIApplication.shared.canOpenURL(URL(string: "whatsapp://send?text=test")!) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
static func share(text: String, complection: @escaping (_ isOpened: Bool)->() = {_ in }) {
|
||||
let urlStringEncoded = text.addingPercentEncoding( withAllowedCharacters: .urlHostAllowed)
|
||||
let urlOptional = URL(string: "whatsapp://send?text=\(urlStringEncoded ?? "")")
|
||||
if let url = urlOptional {
|
||||
if UIApplication.shared.canOpenURL(url) {
|
||||
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: complection)
|
||||
} else {
|
||||
complection(false)
|
||||
}
|
||||
} else {
|
||||
complection(false)
|
||||
}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
// Helper function inserted by Swift 4.2 migrator.
|
||||
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
|
||||
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
import CoreSpotlight
|
||||
import MobileCoreServices
|
||||
|
||||
struct SPSpotlight {
|
||||
|
||||
static let domainIdentifier = "by.ivanvorobei"
|
||||
|
||||
static func addItem(identifier: String, title: String, description: String, addedData: Date? = nil, keywords: [String] = []) {
|
||||
if #available(iOS 9.0, *) {
|
||||
let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeMessage as String)
|
||||
|
||||
attributeSet.title = title
|
||||
attributeSet.contentDescription = description
|
||||
attributeSet.keywords = keywords
|
||||
if addedData != nil {
|
||||
attributeSet.contentCreationDate = addedData!
|
||||
}
|
||||
|
||||
let item = CSSearchableItem(uniqueIdentifier: "\(identifier)", domainIdentifier: SPSpotlight.domainIdentifier, attributeSet: attributeSet)
|
||||
|
||||
CSSearchableIndex.default().indexSearchableItems([item]) { error in
|
||||
if let error = error {
|
||||
print("SPSpotlight addItem error: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func removeItem(identifier: String) {
|
||||
if #available(iOS 9.0, *) {
|
||||
CSSearchableIndex.default().deleteSearchableItems(withIdentifiers: ["\(identifier)"]) { error in
|
||||
if let error = error {
|
||||
print("SPSpotlight removeItem error: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public struct SPNativeStyleKit {
|
||||
|
||||
struct Colors {
|
||||
|
||||
static let red = UIColor.init(hex: "FF3B30")
|
||||
static let orange = UIColor.init(hex: "FF9500")
|
||||
static let yellow = UIColor.init(hex: "FFCC00")
|
||||
static let green = UIColor.init(hex: "4CD964")
|
||||
static let tealBlue = UIColor.init(hex: "5AC8FA")
|
||||
static let blue = UIColor.init(hex: "007AFF")
|
||||
static let purple = UIColor.init(hex: "5856D6")
|
||||
static let pink = UIColor.init(hex: "FF2D55")
|
||||
static let white = UIColor.init(hex: "FFFFFF")
|
||||
static let customGray = UIColor.init(hex: "EFEFF4")
|
||||
static let lightGray = UIColor.init(hex: "E5E5EA")
|
||||
static let lightGray2 = UIColor.init(hex: "D1D1D6")
|
||||
static let midGray = UIColor.init(hex: "C7C7CC")
|
||||
static let gray = UIColor.init(hex: "8E8E93")
|
||||
static let black = UIColor.init(hex: "000000")
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public enum SPStatusBar {
|
||||
case dark
|
||||
case light
|
||||
}
|
||||
|
||||
public enum SPSnapToSide {
|
||||
case left
|
||||
case right
|
||||
}
|
||||
|
||||
public enum SPSystemIconType {
|
||||
case share
|
||||
case close
|
||||
case favorite
|
||||
case favorite_fill
|
||||
}
|
||||
|
||||
public enum SPSocialNetwork {
|
||||
case whatsapp
|
||||
case telegram
|
||||
case vk
|
||||
case facebook
|
||||
case viber
|
||||
}
|
||||
|
||||
public enum SPOauthState {
|
||||
case succsess
|
||||
case unvalidLogin
|
||||
case invalidLogin
|
||||
case unvalidPassword
|
||||
case invalidPassword
|
||||
case needTwoFactor
|
||||
case error
|
||||
}
|
||||
|
||||
public enum SPSeparatorInsetStyle {
|
||||
case beforeImage
|
||||
case all
|
||||
case none
|
||||
case auto
|
||||
}
|
||||
|
||||
public enum SPNavigationTitleStyle {
|
||||
case large
|
||||
case small
|
||||
case stork
|
||||
}
|
||||
|
||||
public enum SPSystemApp {
|
||||
case photos
|
||||
case setting
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPAppStoreActionButton: SPDownloadingButton {
|
||||
|
||||
var style: Style = .base {
|
||||
didSet {
|
||||
|
||||
self.setTitleColor(self.baseColor)
|
||||
self.setTitle(self.titleLabel?.text, for: UIControl.State.normal)
|
||||
|
||||
switch self.style {
|
||||
case .base:
|
||||
self.backgroundColor = self.secondColor
|
||||
self.titleLabel?.font = UIFont.system(type: .Bold, size: 14)
|
||||
self.contentEdgeInsets = UIEdgeInsets.init(top: 6, left: 15, bottom: 6, right: 15)
|
||||
break
|
||||
case .main:
|
||||
self.backgroundColor = self.baseColor
|
||||
self.layer.borderWidth = 0
|
||||
self.setTitleColor(UIColor.white)
|
||||
self.titleLabel?.font = UIFont.system(type: .Bold, size: 14)
|
||||
self.contentEdgeInsets = UIEdgeInsets.init(top: 6, left: 15, bottom: 6, right: 15)
|
||||
break
|
||||
case .buyInStore:
|
||||
self.backgroundColor = self.baseColor
|
||||
self.layer.borderWidth = 0
|
||||
self.setTitleColor(UIColor.white)
|
||||
self.titleLabel?.font = UIFont.system(type: .Bold, size: 14)
|
||||
self.contentEdgeInsets = UIEdgeInsets.init(top: 8, left: 15, bottom: 8, right: 15)
|
||||
break
|
||||
case .line:
|
||||
self.backgroundColor = UIColor.clear
|
||||
self.layer.borderWidth = 1
|
||||
self.layer.borderColor = self.baseColor.cgColor
|
||||
self.titleLabel?.font = UIFont.system(type: .Medium, size: 14)
|
||||
self.contentEdgeInsets = UIEdgeInsets.init(top: 6, left: 15, bottom: 6, right: 15)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var baseColor: UIColor = UIColor.init(hex: "0076FF") {
|
||||
didSet {
|
||||
let currentStyle = self.style
|
||||
self.style = currentStyle
|
||||
}
|
||||
}
|
||||
var secondColor: UIColor = UIColor.init(hex: "F0F1F6") {
|
||||
didSet {
|
||||
let currentStyle = self.style
|
||||
self.style = currentStyle
|
||||
}
|
||||
}
|
||||
|
||||
override func commonInit() {
|
||||
self.style = .base
|
||||
self.layer.masksToBounds = true
|
||||
}
|
||||
|
||||
override func setTitle(_ title: String?, for state: UIControl.State) {
|
||||
switch self.style {
|
||||
case .base:
|
||||
super.setTitle(title?.uppercased(), for: state)
|
||||
case .main:
|
||||
super.setTitle(title?.uppercased(), for: state)
|
||||
case .buyInStore:
|
||||
super.setTitle(title?.uppercasedFirstLetter(), for: state)
|
||||
case .line:
|
||||
super.setTitle(title?.uppercased(), for: state)
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
if self.style == .buyInStore {
|
||||
self.layer.cornerRadius = 12
|
||||
} else {
|
||||
self.round()
|
||||
}
|
||||
|
||||
if self.style != .main {
|
||||
if let count = self.titleLabel?.text?.count {
|
||||
if count < 4 {
|
||||
self.contentEdgeInsets = UIEdgeInsets.init(top: 6, left: 22, bottom: 6, right: 22)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Style {
|
||||
case base
|
||||
case main
|
||||
case buyInStore
|
||||
case line
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPAppleMusicButton: SPButton {
|
||||
|
||||
var mode: Mode = .unselect {
|
||||
didSet {
|
||||
self.updateStyle(animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
var selectColor: UIColor = UIColor.init(hex: "FD2D55") {
|
||||
didSet {
|
||||
self.updateStyle(animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
var baseColor: UIColor = UIColor.init(hex: "F8F7FC") {
|
||||
didSet {
|
||||
self.updateStyle(animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
override func commonInit() {
|
||||
super.commonInit()
|
||||
self.layer.cornerRadius = 8
|
||||
self.titleLabel?.font = UIFont.system(type: .DemiBold, size: 15)
|
||||
self.contentEdgeInsets = UIEdgeInsets.init(top: 12, left: 27, bottom: 12, right: 27)
|
||||
self.mode = .unselect
|
||||
}
|
||||
|
||||
private func updateStyle(animated: Bool) {
|
||||
switch self.mode {
|
||||
case .select:
|
||||
self.backgroundColor = self.selectColor
|
||||
self.setTitleColor(UIColor.white)
|
||||
break
|
||||
case .unselect:
|
||||
self.backgroundColor = self.baseColor
|
||||
self.setTitleColor(self.selectColor)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
case select
|
||||
case unselect
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPButton: UIButton {
|
||||
|
||||
var gradientView: SPGradientView? {
|
||||
didSet {
|
||||
self.gradientView?.isUserInteractionEnabled = false
|
||||
if self.gradientView?.superview == nil {
|
||||
if self.gradientView != nil {
|
||||
self.insertSubview(self.gradientView!, at: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var round: Bool = false {
|
||||
didSet {
|
||||
self.layoutSubviews()
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
super.init(frame: CGRect.zero)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
internal func commonInit() {}
|
||||
|
||||
override public func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.gradientView?.setEqualsBoundsFromSuperview()
|
||||
if self.round {
|
||||
self.round()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPDotButton: SPButton {
|
||||
|
||||
var customSideSize: CGFloat = 26 {
|
||||
didSet {
|
||||
self.sizeToFit()
|
||||
}
|
||||
}
|
||||
|
||||
var dotColor: UIColor = UIColor.white {
|
||||
didSet {
|
||||
for dotView in self.dotsView {
|
||||
dotView.backgroundColor = self.dotColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override var isHighlighted: Bool{
|
||||
didSet{
|
||||
if isHighlighted{
|
||||
UIView.animate(withDuration: 0.1, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 1.0, options: [.curveEaseOut, .beginFromCurrentState], animations: {
|
||||
for dotView in self.dotsView {
|
||||
dotView.alpha = 0.35
|
||||
}
|
||||
}, completion: nil)
|
||||
}else{
|
||||
UIView.animate(withDuration: 0.35, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 1.0, options: [.curveEaseOut, .beginFromCurrentState], animations: {
|
||||
for dotView in self.dotsView {
|
||||
dotView.alpha = 1
|
||||
}
|
||||
}, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var dotsView: [UIView] = []
|
||||
|
||||
override func commonInit() {
|
||||
super.commonInit()
|
||||
self.backgroundColor = UIColor.black.withAlphaComponent(0.2)
|
||||
for _ in 0...2 {
|
||||
let dotView = UIView()
|
||||
dotView.isUserInteractionEnabled = false
|
||||
dotView.backgroundColor = self.dotColor
|
||||
self.dotsView.append(dotView)
|
||||
self.addSubview(dotView)
|
||||
}
|
||||
}
|
||||
|
||||
override func sizeToFit() {
|
||||
super.sizeToFit()
|
||||
self.setWidth(self.customSideSize)
|
||||
self.setHeight(self.customSideSize)
|
||||
self.layoutSubviews()
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
let space: CGFloat = 2
|
||||
let sideSize: CGFloat = 4
|
||||
|
||||
let insest: CGFloat = (self.frame.width - (sideSize * 3) - (space * 2)) / 2
|
||||
|
||||
var currentXPosition: CGFloat = insest
|
||||
|
||||
for dotView in self.dotsView {
|
||||
dotView.setWidth(sideSize)
|
||||
dotView.setHeight(sideSize)
|
||||
dotView.setYCenteringFromSuperview()
|
||||
dotView.frame.origin.x = currentXPosition
|
||||
dotView.round()
|
||||
currentXPosition += (sideSize + space)
|
||||
}
|
||||
|
||||
self.round()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPDownloadingButton: SPButton {
|
||||
|
||||
let activityIndicatorView = UIActivityIndicatorView.init()
|
||||
var isFrameRounded: Bool = false
|
||||
|
||||
func startLoading() {
|
||||
self.activityIndicatorView.alpha = 0
|
||||
self.activityIndicatorView.isHidden = false
|
||||
self.activityIndicatorView.startAnimating()
|
||||
self.activityIndicatorView.color = self.titleLabel?.textColor
|
||||
self.addSubview(self.activityIndicatorView)
|
||||
self.hideContent(completion: {
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
self.activityIndicatorView.alpha = 1
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func stopLoading() {
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
self.activityIndicatorView.alpha = 0
|
||||
}, withComplection: {
|
||||
self.activityIndicatorView.removeFromSuperview()
|
||||
self.activityIndicatorView.isHidden = true
|
||||
self.activityIndicatorView.stopAnimating()
|
||||
self.showContent()
|
||||
})
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.activityIndicatorView.center = CGPoint.init(x: self.frame.width / 2, y: self.frame.height / 2)
|
||||
|
||||
if self.isFrameRounded {
|
||||
self.round()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPNativeLargeButton: SPDownloadingButton {
|
||||
|
||||
override var isHighlighted: Bool {
|
||||
didSet {
|
||||
if self.gradientView == nil {
|
||||
if isHighlighted {
|
||||
self.backgroundColor = self.backgroundColor?.withAlphaComponent(0.7)
|
||||
} else {
|
||||
self.backgroundColor = self.backgroundColor?.withAlphaComponent(1)
|
||||
}
|
||||
} else {
|
||||
self.backgroundColor = self.backgroundColor?.withAlphaComponent(0)
|
||||
if isHighlighted {
|
||||
self.gradientView?.alpha = 0.7
|
||||
} else {
|
||||
self.gradientView?.alpha = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func commonInit() {
|
||||
super.commonInit()
|
||||
self.titleLabel?.font = UIFont.system(type: UIFont.BoldType.DemiBold, size: 16)
|
||||
self.setTitleColor(UIColor.white)
|
||||
self.backgroundColor = SPNativeStyleKit.Colors.blue
|
||||
self.layer.masksToBounds = true
|
||||
self.layer.cornerRadius = 8
|
||||
self.contentEdgeInsets = UIEdgeInsets.init(top: 15, left: 15, bottom: 15, right: 15)
|
||||
}
|
||||
|
||||
override func sizeToFit() {
|
||||
super.sizeToFit()
|
||||
if let superview = self.superview {
|
||||
let sideSpace: CGFloat = superview.frame.width * 0.112
|
||||
var width = superview.frame.width - sideSpace * 2
|
||||
width.setIfMore(when: 335)
|
||||
self.setWidth(width)
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.gradientView?.setEqualsBoundsFromSuperview()
|
||||
self.gradientView?.layer.cornerRadius = self.layer.cornerRadius
|
||||
self.gradientView?.gradientLayer.cornerRadius = self.layer.cornerRadius
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPPlayCircleButton: UIButton {
|
||||
|
||||
var audioState: AudioState = AudioState.play {
|
||||
didSet {
|
||||
switch self.audioState {
|
||||
case .play:
|
||||
self.iconView.type = .play
|
||||
break
|
||||
case .pause:
|
||||
self.iconView.type = .pause
|
||||
break
|
||||
case .stop:
|
||||
self.iconView.type = .stop
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var iconColor = SPNativeStyleKit.Colors.white {
|
||||
didSet {
|
||||
self.iconView.color = self.iconColor
|
||||
}
|
||||
}
|
||||
|
||||
let iconView = SPAudioIconView.init()
|
||||
|
||||
init() {
|
||||
super.init(frame: CGRect.zero)
|
||||
self.addSubview(self.iconView)
|
||||
self.iconView.isUserInteractionEnabled = false
|
||||
self.setTitle("", for: .normal)
|
||||
self.backgroundColor = SPNativeStyleKit.Colors.blue
|
||||
self.audioState = .play
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.iconView.setEqualsFrameFromBounds(self, withWidthFactor: 0.45, withHeightFactor: 0.45, withCentering: true)
|
||||
self.round()
|
||||
}
|
||||
|
||||
enum AudioState {
|
||||
case play
|
||||
case pause
|
||||
case stop
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPSocialButton: UIButton {
|
||||
|
||||
let iconView = SPSocialIconView.init()
|
||||
var widthIconFactor: CGFloat = 0.5
|
||||
var heightIconFactor: CGFloat = 0.5
|
||||
|
||||
var type: SPSocialNetwork {
|
||||
didSet {
|
||||
self.iconView.type = self.type
|
||||
}
|
||||
}
|
||||
|
||||
override var isHighlighted: Bool {
|
||||
didSet {
|
||||
if isHighlighted {
|
||||
self.iconView.color = self.iconView.color.withAlphaComponent(0.7)
|
||||
} else {
|
||||
self.iconView.color = self.iconView.color.withAlphaComponent(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override var isEnabled: Bool {
|
||||
didSet {
|
||||
if isEnabled {
|
||||
self.alpha = 1
|
||||
} else {
|
||||
self.alpha = 0.5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
self.type = .facebook
|
||||
super.init(frame: CGRect.zero)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
init(type: SPSocialNetwork) {
|
||||
self.type = type
|
||||
super.init(frame: CGRect.zero)
|
||||
self.commonInit()
|
||||
defer {
|
||||
self.type = type
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
fileprivate func commonInit() {
|
||||
self.iconView.isUserInteractionEnabled = false
|
||||
self.addSubview(self.iconView)
|
||||
self.backgroundColor = SPNativeStyleKit.Colors.blue
|
||||
self.iconView.color = SPNativeStyleKit.Colors.white
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.iconView.setEqualsFrameFromBounds(self, withWidthFactor: self.widthIconFactor, withHeightFactor: self.heightIconFactor, withCentering: true)
|
||||
self.round()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPSystemIconButton: UIButton {
|
||||
|
||||
let iconView = SPSystemIconView.init()
|
||||
var widthIconFactor: CGFloat = 1
|
||||
var heightIconFactor: CGFloat = 1
|
||||
|
||||
var type: SPSystemIconType {
|
||||
didSet {
|
||||
self.iconView.type = self.type
|
||||
}
|
||||
}
|
||||
|
||||
var color = SPNativeStyleKit.Colors.blue {
|
||||
didSet {
|
||||
self.iconView.color = self.color
|
||||
}
|
||||
}
|
||||
|
||||
override var isHighlighted: Bool {
|
||||
didSet {
|
||||
if isHighlighted {
|
||||
self.iconView.color = self.color.withAlphaComponent(0.7)
|
||||
} else {
|
||||
self.iconView.color = self.color.withAlphaComponent(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
self.type = .share
|
||||
super.init(frame: CGRect.zero)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
init(type: SPSystemIconType) {
|
||||
self.type = type
|
||||
super.init(frame: CGRect.zero)
|
||||
self.iconView.type = self.type
|
||||
self.type = type
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
fileprivate func commonInit() {
|
||||
self.iconView.isUserInteractionEnabled = false
|
||||
self.addSubview(self.iconView)
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.iconView.setEqualsFrameFromBounds(self, withWidthFactor: self.widthIconFactor, withHeightFactor: self.heightIconFactor, withCentering: true)
|
||||
}
|
||||
}
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPNativeLoginCodeViewController: SPNativeTableViewController {
|
||||
|
||||
weak var delegate: SPLoginCodeControllerDelegate?
|
||||
var content: SPNativeLoginNavigationController.LoginCodeContent!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.delegate = self.navigationController as! SPNativeLoginNavigationController
|
||||
self.content = self.delegate?.loginCodeContent
|
||||
|
||||
self.setPrefersLargeNavigationTitle(self.content.navigationTitle)
|
||||
self.dismissKeyboardWhenTappedAround()
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
if let cell = self.tableView.cellForRow(at: IndexPath.init(row: 0, section: 0)) as? SPFormTextFiledTableViewCell {
|
||||
cell.textField.becomeFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
switch section {
|
||||
case 0:
|
||||
return 1
|
||||
case 1:
|
||||
return 1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return 2
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
|
||||
var labelWidth: CGFloat = 0
|
||||
for text in [self.content.codeTitle] {
|
||||
let font = UIFont.system(type: .Regular, size: 17)
|
||||
let fontAttributes = [NSAttributedString.Key.font: font]
|
||||
let calculatedSize = NSString.init(string: text).size(withAttributes: fontAttributes)
|
||||
labelWidth.setIfFewer(when: calculatedSize.width + 1)
|
||||
}
|
||||
|
||||
switch indexPath {
|
||||
case IndexPath.init(row: 0, section: 0):
|
||||
let cell = self.dequeueTextFiledTableViewCell(indexPath: indexPath)
|
||||
cell.label.text = self.content.codeTitle
|
||||
cell.textField.placeholder = self.content.codePlaceholder
|
||||
cell.textField.keyboardType = self.content.codeKeyboardType
|
||||
cell.textField.delegate = self
|
||||
cell.textField.returnKeyType = .done
|
||||
cell.textField.autocapitalizationType = .none
|
||||
cell.textField.autocorrectionType = .no
|
||||
cell.textField.isSecureTextEntry = true
|
||||
cell.fixWidthLabel = labelWidth
|
||||
cell.separatorInsetStyle = .all
|
||||
cell.textAligmentToSide = false
|
||||
return cell
|
||||
case IndexPath.init(row: 0, section: 1):
|
||||
let cell = self.dequeueButtonTableViewCell(indexPath: indexPath)
|
||||
cell.button.setTitle(self.content.buttonTitle, for: .normal)
|
||||
cell.separatorInset.left = 0
|
||||
cell.button.addTarget(self, action: #selector(self.enterAction), for: .touchUpInside)
|
||||
return cell
|
||||
default:
|
||||
return UITableViewCell()
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||
switch section {
|
||||
case 0:
|
||||
return self.content.commentTitle
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@objc func enterAction() {
|
||||
var сodeCell: SPFormTextFiledTableViewCell? = nil
|
||||
var buttonCell: SPFormButtonTableViewCell? = nil
|
||||
|
||||
if let cell = self.tableView.cellForRow(at: IndexPath.init(row: 0, section: 0)) as? SPFormTextFiledTableViewCell {
|
||||
сodeCell = cell
|
||||
}
|
||||
|
||||
if сodeCell?.textField.isEmptyText ?? false {
|
||||
сodeCell?.highlight()
|
||||
return
|
||||
}
|
||||
|
||||
if let cell = self.tableView.cellForRow(at: IndexPath.init(row: 0, section: 1)) as? SPFormButtonTableViewCell {
|
||||
buttonCell = cell
|
||||
}
|
||||
|
||||
buttonCell?.button.startLoading()
|
||||
|
||||
сodeCell?.textField.isEnabled = false
|
||||
|
||||
self.delegate?.login(with: сodeCell?.textField.text ?? "") { (oAuthState) in
|
||||
|
||||
buttonCell?.button.stopLoading()
|
||||
|
||||
if oAuthState != SPOauthState.succsess {
|
||||
UIAlertController.show(
|
||||
title: self.content.errorOauthTitle,
|
||||
message: self.content.errorOauthSubtitle,
|
||||
buttonTitle: self.content.errorOauthButtonTitle,
|
||||
on: self
|
||||
)
|
||||
сodeCell?.textField.isEnabled = true
|
||||
сodeCell?.textField.becomeFirstResponder()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SPNativeLoginCodeViewController: UITextFieldDelegate {
|
||||
|
||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
textField.resignFirstResponder()
|
||||
self.enterAction()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
protocol SPLoginCodeControllerDelegate: class {
|
||||
|
||||
var loginCodeContent: SPNativeLoginNavigationController.LoginCodeContent {get}
|
||||
|
||||
func login(with code: String, complection: @escaping (SPOauthState)->())
|
||||
}
|
||||
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPNativeLoginNavigationController: UINavigationController, SPLoginControllerDelegate, SPLoginCodeControllerDelegate {
|
||||
|
||||
let loginViewController = SPNativeLoginViewController()
|
||||
let codeViewController = SPNativeLoginCodeViewController()
|
||||
|
||||
init() {
|
||||
super.init(rootViewController: self.loginViewController)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
|
||||
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
|
||||
}
|
||||
|
||||
var loginContent: SPNativeLoginNavigationController.LoginContent {
|
||||
return LoginContent()
|
||||
}
|
||||
|
||||
var loginCodeContent: SPNativeLoginNavigationController.LoginCodeContent {
|
||||
return LoginCodeContent()
|
||||
}
|
||||
|
||||
struct LoginContent {
|
||||
var navigationTitle: String = "Sign In"
|
||||
var loginTitle: String = "Login"
|
||||
var loginPlaceholder: String = "example@icloud.com"
|
||||
var loginKeyboardType: UIKeyboardType = .emailAddress
|
||||
var passwordTitle: String = "Password"
|
||||
var passwordPlaceholder: String = "Required"
|
||||
var commentTitle: String = "Please enter a pair of login and password"
|
||||
var buttonTitle: String = "Sign In"
|
||||
var errorOauthTitle: String = "Error"
|
||||
var errorOauthSubtitle: String = "Invalid login or password"
|
||||
var errorOauthButtonTitle: String = "Ok"
|
||||
}
|
||||
|
||||
struct LoginCodeContent {
|
||||
var navigationTitle: String = "Auth Code"
|
||||
var codeTitle: String = "Code"
|
||||
var codePlaceholder: String = "Required"
|
||||
var codeKeyboardType: UIKeyboardType = .numberPad
|
||||
var commentTitle: String = "Please enter a code for authentication"
|
||||
var buttonTitle: String = "Sign In"
|
||||
var errorOauthTitle: String = "Error"
|
||||
var errorOauthSubtitle: String = "Invalid data"
|
||||
var errorOauthButtonTitle: String = "Ok"
|
||||
}
|
||||
|
||||
func login(with login: String, password: String, complection: @escaping (SPOauthState) -> ()) {
|
||||
fatalError("SPLoginNavigationController - Need override func")
|
||||
}
|
||||
|
||||
func login(with code: String, complection: @escaping (SPOauthState) -> ()) {
|
||||
fatalError("SPLoginNavigationController - Need override func")
|
||||
}
|
||||
|
||||
func needRequestCode() {
|
||||
self.pushViewController(self.codeViewController, animated: true)
|
||||
}
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPNativeLoginViewController: SPNativeTableViewController {
|
||||
|
||||
weak var delegate: SPLoginControllerDelegate?
|
||||
var content: SPNativeLoginNavigationController.LoginContent!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.delegate = self.navigationController as! SPNativeLoginNavigationController
|
||||
self.content = self.delegate?.loginContent
|
||||
|
||||
self.setPrefersLargeNavigationTitle(self.content.navigationTitle)
|
||||
self.dismissKeyboardWhenTappedAround()
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
if let cell = self.tableView.cellForRow(at: IndexPath.init(row: 0, section: 0)) as? SPFormTextFiledTableViewCell {
|
||||
cell.textField.becomeFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
switch section {
|
||||
case 0:
|
||||
return 2
|
||||
case 1:
|
||||
return 1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return 2
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
|
||||
var labelWidth: CGFloat = 0
|
||||
for text in [self.content.loginTitle, self.content.passwordTitle] {
|
||||
let font = UIFont.system(type: .Regular, size: 17)
|
||||
let fontAttributes = [NSAttributedString.Key.font: font]
|
||||
let calculatedSize = NSString.init(string: text).size(withAttributes: fontAttributes)
|
||||
labelWidth.setIfFewer(when: calculatedSize.width + 1)
|
||||
}
|
||||
|
||||
switch indexPath {
|
||||
case IndexPath.init(row: 0, section: 0):
|
||||
let cell = self.dequeueTextFiledTableViewCell(indexPath: indexPath)
|
||||
cell.label.text = self.content.loginTitle
|
||||
cell.textField.placeholder = self.content.loginPlaceholder
|
||||
cell.textField.keyboardType = self.content.loginKeyboardType
|
||||
cell.textField.delegate = self
|
||||
cell.textField.returnKeyType = .next
|
||||
cell.textField.autocapitalizationType = .none
|
||||
cell.textField.autocorrectionType = .no
|
||||
cell.fixWidthLabel = labelWidth
|
||||
cell.textAligmentToSide = false
|
||||
return cell
|
||||
case IndexPath.init(row: 1, section: 0):
|
||||
let cell = self.dequeueTextFiledTableViewCell(indexPath: indexPath)
|
||||
cell.label.text = self.content.passwordTitle
|
||||
cell.textField.placeholder = self.content.passwordPlaceholder
|
||||
cell.textField.isSecureTextEntry = true
|
||||
cell.textField.delegate = self
|
||||
cell.textField.returnKeyType = .done
|
||||
cell.textField.autocapitalizationType = .none
|
||||
cell.textField.autocorrectionType = .no
|
||||
cell.separatorInset.left = 0
|
||||
cell.fixWidthLabel = labelWidth
|
||||
cell.separatorInsetStyle = .all
|
||||
cell.textAligmentToSide = false
|
||||
return cell
|
||||
case IndexPath.init(row: 0, section: 1):
|
||||
let cell = self.dequeueButtonTableViewCell(indexPath: indexPath)
|
||||
cell.button.setTitle(self.content.buttonTitle, for: .normal)
|
||||
cell.separatorInset.left = 0
|
||||
cell.button.addTarget(self, action: #selector(self.enterAction), for: .touchUpInside)
|
||||
return cell
|
||||
default:
|
||||
return UITableViewCell()
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||
switch section {
|
||||
case 0:
|
||||
return self.content.commentTitle
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@objc func enterAction() {
|
||||
var loginCell: SPFormTextFiledTableViewCell? = nil
|
||||
var passwordCell: SPFormTextFiledTableViewCell? = nil
|
||||
var buttonCell: SPFormButtonTableViewCell? = nil
|
||||
|
||||
if let cell = self.tableView.cellForRow(at: IndexPath.init(row: 0, section: 0)) as? SPFormTextFiledTableViewCell {
|
||||
loginCell = cell
|
||||
}
|
||||
if let cell = self.tableView.cellForRow(at: IndexPath.init(row: 1, section: 0)) as? SPFormTextFiledTableViewCell {
|
||||
passwordCell = cell
|
||||
}
|
||||
if let cell = self.tableView.cellForRow(at: IndexPath.init(row: 0, section: 1)) as? SPFormButtonTableViewCell {
|
||||
buttonCell = cell
|
||||
}
|
||||
|
||||
if loginCell?.textField.isEmptyText ?? false {
|
||||
loginCell?.highlight()
|
||||
return
|
||||
}
|
||||
|
||||
if passwordCell?.textField.isEmptyText ?? false {
|
||||
passwordCell?.highlight()
|
||||
return
|
||||
}
|
||||
|
||||
buttonCell?.button.startLoading()
|
||||
|
||||
loginCell?.textField.isEnabled = false
|
||||
passwordCell?.textField.isEnabled = false
|
||||
|
||||
self.delegate?.login(with: loginCell?.textField.text ?? "", password: passwordCell?.textField.text ?? "") { (oAuthState) in
|
||||
|
||||
buttonCell?.button.stopLoading()
|
||||
|
||||
switch oAuthState {
|
||||
case .succsess:
|
||||
break
|
||||
case .needTwoFactor:
|
||||
self.delegate?.needRequestCode()
|
||||
break
|
||||
default:
|
||||
loginCell?.textField.isEnabled = true
|
||||
passwordCell?.textField.isEnabled = true
|
||||
passwordCell?.textField.becomeFirstResponder()
|
||||
UIAlertController.show(
|
||||
title: self.content.errorOauthTitle,
|
||||
message: self.content.errorOauthSubtitle,
|
||||
buttonTitle: self.content.errorOauthButtonTitle,
|
||||
on: self
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SPNativeLoginViewController: UITextFieldDelegate {
|
||||
|
||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
if textField.superview == self.tableView.cellForRow(at: IndexPath.init(row: 0, section: 0))?.contentView {
|
||||
if let passwordCell = self.tableView.cellForRow(at: IndexPath.init(row: 1, section: 0)) as? SPFormTextFiledTableViewCell {
|
||||
passwordCell.textField.becomeFirstResponder()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if textField.superview == self.tableView.cellForRow(at: IndexPath.init(row: 1, section: 0))?.contentView {
|
||||
textField.resignFirstResponder()
|
||||
self.enterAction()
|
||||
return true
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
protocol SPLoginControllerDelegate: class {
|
||||
|
||||
var loginContent: SPNativeLoginNavigationController.LoginContent {get}
|
||||
|
||||
func login(with login: String, password: String, complection: @escaping (SPOauthState)->())
|
||||
|
||||
func needRequestCode() -> ()
|
||||
}
|
||||
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPBaseTableViewController: SPStatusBarManagerTableViewController {
|
||||
|
||||
var activityIndicatorView = UIActivityIndicatorView.init()
|
||||
var searchController: UISearchController?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidShow(notification:)), name: UIResponder.keyboardDidShowNotification, object: nil)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidHide(notification:)), name: UIResponder.keyboardDidHideNotification, object: nil)
|
||||
|
||||
self.activityIndicatorView.stopAnimating()
|
||||
self.activityIndicatorView.color = SPNativeStyleKit.Colors.gray
|
||||
self.view.addSubview(self.activityIndicatorView)
|
||||
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
}
|
||||
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
coordinator.animate(alongsideTransition: { (contex) in
|
||||
self.updateLayout(with: size)
|
||||
}, completion: nil)
|
||||
}
|
||||
|
||||
@available(iOS 11.0, *)
|
||||
override func viewLayoutMarginsDidChange() {
|
||||
super.viewLayoutMarginsDidChange()
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
}
|
||||
|
||||
func updateLayout(with size: CGSize) {
|
||||
let layoutIfShowKeyboard = {
|
||||
let height = size.height - (self.keyboardSize?.height ?? 0) - self.safeArea.top
|
||||
self.emptyProposeView?.frame = CGRect.init(
|
||||
x: 0, y: 0,
|
||||
width: size.width * self.emptyProposeViewWidthFactor,
|
||||
height: height
|
||||
)
|
||||
self.emptyProposeView?.center.x = size.width / 2
|
||||
}
|
||||
|
||||
let layoutIfNotShowKeyboard = {
|
||||
let height = size.height - self.safeArea.top - self.safeArea.bottom
|
||||
self.emptyProposeView?.frame = CGRect.init(
|
||||
x: 0, y: 0,
|
||||
width: size.width * self.emptyProposeViewWidthFactor,
|
||||
height: height
|
||||
)
|
||||
self.emptyProposeView?.center.x = size.width / 2
|
||||
}
|
||||
|
||||
if self.isShowKeyboard {
|
||||
if self.isAllowLayoutWithKeyboardEvents {
|
||||
layoutIfShowKeyboard()
|
||||
} else {
|
||||
layoutIfNotShowKeyboard()
|
||||
}
|
||||
} else {
|
||||
layoutIfNotShowKeyboard()
|
||||
}
|
||||
|
||||
if let center = self.emptyProposeView?.center {
|
||||
self.activityIndicatorView.center = center
|
||||
} else {
|
||||
self.activityIndicatorView.center = CGPoint.init(
|
||||
x: size.width / 2,
|
||||
y: (size.height - self.safeArea.top - self.safeArea.bottom) / 2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Keyboard
|
||||
var isShowKeyboard: Bool = false
|
||||
var isAllowLayoutWithKeyboardEvents: Bool = false
|
||||
var keyboardSize: CGSize? = nil
|
||||
|
||||
func keyboardWillShow(duration: TimeInterval) {
|
||||
SPAnimation.animate(duration, animations: {
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
})
|
||||
}
|
||||
|
||||
func keyboardDidShow(duration: TimeInterval) {
|
||||
SPAnimation.animate(duration, animations: {
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
})
|
||||
}
|
||||
|
||||
func keyboardWillHide(duration: TimeInterval) {
|
||||
SPAnimation.animate(duration, animations: {
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
})
|
||||
}
|
||||
|
||||
func keyboardDidHide(duration: TimeInterval) {
|
||||
SPAnimation.animate(duration, animations: {
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
})
|
||||
}
|
||||
|
||||
@objc func keyboardWillShow(notification: NSNotification) {
|
||||
self.isShowKeyboard = true
|
||||
self.keyboardSize = self.keyboardSize(from: notification)
|
||||
let duration = self.keyboardAnimateDuration(from: notification) ?? 0.3
|
||||
self.keyboardWillShow(duration: duration)
|
||||
}
|
||||
|
||||
@objc func keyboardDidShow(notification: NSNotification) {
|
||||
self.isShowKeyboard = true
|
||||
self.keyboardSize = self.keyboardSize(from: notification)
|
||||
let duration = self.keyboardAnimateDuration(from: notification) ?? 0.3
|
||||
self.keyboardDidShow(duration: duration)
|
||||
}
|
||||
|
||||
@objc func keyboardWillHide(notification: NSNotification) {
|
||||
self.isShowKeyboard = false
|
||||
self.keyboardSize = nil
|
||||
let duration = self.keyboardAnimateDuration(from: notification) ?? 0.3
|
||||
self.keyboardWillHide(duration: duration)
|
||||
}
|
||||
|
||||
@objc func keyboardDidHide(notification: NSNotification) {
|
||||
self.isShowKeyboard = false
|
||||
self.keyboardSize = nil
|
||||
let duration = self.keyboardAnimateDuration(from: notification) ?? 0.3
|
||||
self.keyboardDidHide(duration: duration)
|
||||
}
|
||||
|
||||
func keyboardSize(from notification: NSNotification) -> CGSize? {
|
||||
let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue
|
||||
return keyboardFrame?.cgRectValue.size
|
||||
}
|
||||
|
||||
func keyboardAnimateDuration(from notification: NSNotification) -> TimeInterval? {
|
||||
return notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval
|
||||
}
|
||||
|
||||
//MARK: - Empty Propose View
|
||||
var emptyProposeViewYTranslateFactor: CGFloat = 0.98
|
||||
var emptyProposeViewWidthFactor: CGFloat = 0.7
|
||||
private var emptyProposeView: UIView? {
|
||||
didSet {
|
||||
if self.emptyProposeView != nil {
|
||||
self.view.addSubview(self.emptyProposeView!)
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setEmptyProposeView(_ view: UIView) {
|
||||
self.emptyProposeView = view
|
||||
self.hideEmptyProposeView(animated: false)
|
||||
}
|
||||
|
||||
func hideEmptyProposeView(animated: Bool) {
|
||||
let hideFunc = {
|
||||
self.emptyProposeView?.isHidden = true
|
||||
self.emptyProposeView?.alpha = 0
|
||||
}
|
||||
|
||||
if animated {
|
||||
SPAnimation.animate(0.3, animations: {
|
||||
self.emptyProposeView?.alpha = 0
|
||||
}, withComplection: {
|
||||
hideFunc()
|
||||
})
|
||||
} else {
|
||||
hideFunc()
|
||||
}
|
||||
}
|
||||
|
||||
func showEmptyProposeView(animated: Bool) {
|
||||
self.emptyProposeView?.isHidden = false
|
||||
if animated {
|
||||
SPAnimation.animate(0.3, animations: {
|
||||
self.emptyProposeView?.alpha = 1
|
||||
})
|
||||
} else {
|
||||
self.emptyProposeView?.alpha = 1
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Cache
|
||||
private var cacheImages: [(link: String, image: UIImage)] = []
|
||||
|
||||
func toCache(link: String, image: UIImage?) {
|
||||
if image == nil {
|
||||
return
|
||||
}
|
||||
if self.fromCahce(link: link) == nil {
|
||||
self.cacheImages.append((link: link, image: image!))
|
||||
}
|
||||
}
|
||||
|
||||
func fromCahce(link: String) -> UIImage? {
|
||||
let cachedData = self.cacheImages.first(where: {
|
||||
$0.link == link
|
||||
})
|
||||
return cachedData?.image
|
||||
}
|
||||
|
||||
//MARK: - Reload Table View
|
||||
func reloadTableView(animated: Bool, complection: @escaping ()->() = {}) {
|
||||
if animated {
|
||||
UIView.transition(
|
||||
with: self.tableView,
|
||||
duration: 0.3,
|
||||
options: [.transitionCrossDissolve, UIView.AnimationOptions.beginFromCurrentState],
|
||||
animations: {
|
||||
self.tableView.reloadData()
|
||||
}, completion: {(state) in
|
||||
complection()
|
||||
})
|
||||
} else {
|
||||
self.tableView.reloadData()
|
||||
complection()
|
||||
}
|
||||
}
|
||||
|
||||
func startLoading() {
|
||||
if self.tableView.isEmpty {
|
||||
self.activityIndicatorView.startAnimating()
|
||||
if self.refreshControl == nil {
|
||||
self.tableView.isScrollEnabled = false
|
||||
}
|
||||
}
|
||||
self.hideEmptyProposeView(animated: false)
|
||||
}
|
||||
|
||||
func endLoading() {
|
||||
self.activityIndicatorView.stopAnimating()
|
||||
|
||||
if self.tableView.isEmpty {
|
||||
self.reloadTableView(animated: true)
|
||||
} else {
|
||||
self.reloadTableView(animated: false)
|
||||
}
|
||||
|
||||
if self.tableView.isEmpty {
|
||||
if self.refreshControl == nil {
|
||||
self.tableView.isScrollEnabled = false
|
||||
}
|
||||
self.showEmptyProposeView(animated: true)
|
||||
} else {
|
||||
self.tableView.isScrollEnabled = true
|
||||
self.hideEmptyProposeView(animated: true)
|
||||
}
|
||||
if self.refreshControl?.isRefreshing ?? false {
|
||||
delay(0.12, closure: {
|
||||
self.refreshControl?.endRefreshing()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Search
|
||||
@available(iOS 11.0, *)
|
||||
func addSearchController(placeholder: String, resultController: UIViewController?, searchResultsUpdater: UISearchResultsUpdating?, searchBarDelegate: UISearchBarDelegate?) {
|
||||
|
||||
self.searchController = UISearchController(searchResultsController: resultController)
|
||||
self.searchController?.searchBar.placeholder = placeholder
|
||||
self.searchController?.searchResultsUpdater = searchResultsUpdater
|
||||
self.searchController?.searchBar.delegate = searchBarDelegate
|
||||
navigationItem.searchController = searchController
|
||||
definesPresentationContext = true
|
||||
|
||||
self.searchController?.searchBar.tintColor = UINavigationController.elementsColor
|
||||
}
|
||||
|
||||
//MARK: - Other
|
||||
@objc func dismiss(sender: Any) {
|
||||
self.dismiss()
|
||||
}
|
||||
|
||||
func addHideButton(title: String) {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem.init(
|
||||
title: title,
|
||||
style: UIBarButtonItem.Style.done,
|
||||
target: self,
|
||||
action: #selector(self.dismiss(sender:))
|
||||
)
|
||||
}
|
||||
}
|
||||
+245
@@ -0,0 +1,245 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPBaseViewController: SPStatusBarManagerViewController {
|
||||
|
||||
var activityIndicatorLayoutWithSafeArea: Bool = true
|
||||
let activityIndicatorView = UIActivityIndicatorView()
|
||||
|
||||
override public func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.activityIndicatorView.style = .white
|
||||
self.activityIndicatorView.stopAnimating()
|
||||
self.view.addSubview(self.activityIndicatorView)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidShow(notification:)), name: UIResponder.keyboardDidShowNotification, object: nil)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidHide(notification:)), name: UIResponder.keyboardDidHideNotification, object: nil)
|
||||
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
}
|
||||
|
||||
override public func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
coordinator.animate(alongsideTransition: { (contex) in
|
||||
self.updateLayout(with: size)
|
||||
}, completion: nil)
|
||||
}
|
||||
|
||||
@available(iOS 11.0, *)
|
||||
override public func viewLayoutMarginsDidChange() {
|
||||
super.viewLayoutMarginsDidChange()
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
}
|
||||
|
||||
func updateLayout(with size: CGSize) {
|
||||
|
||||
var contentHeight = size.height - self.safeArea.top
|
||||
if self.isShowKeyboard {
|
||||
contentHeight = contentHeight - (self.keyboardSize?.height ?? 0)
|
||||
} else {
|
||||
contentHeight = contentHeight - self.safeArea.bottom
|
||||
}
|
||||
|
||||
let centerYPosition = self.safeArea.top + (contentHeight / 2)
|
||||
|
||||
if self.activityIndicatorLayoutWithSafeArea {
|
||||
self.activityIndicatorView.center = CGPoint.init(
|
||||
x: size.width / 2,
|
||||
y: centerYPosition
|
||||
)
|
||||
} else {
|
||||
self.activityIndicatorView.center = CGPoint.init(
|
||||
x: size.width / 2,
|
||||
y: (size.height - (self.keyboardSize?.height ?? 0)) / 2
|
||||
)
|
||||
}
|
||||
|
||||
var emptyProposeViewWidth = size.width * self.emptyProposeViewWidthFactor
|
||||
if emptyProposeViewWidth < (375 * self.emptyProposeViewWidthFactor) {
|
||||
emptyProposeViewWidth = size.width * 0.9
|
||||
}
|
||||
|
||||
self.emptyProposeView?.frame = CGRect.init(
|
||||
x: 0, y: 0,
|
||||
width: emptyProposeViewWidth,
|
||||
height: (size.height - self.safeArea.top - self.safeArea.bottom) * self.emptyProposeViewHeightFactor
|
||||
)
|
||||
self.emptyProposeView?.center = CGPoint.init(
|
||||
x: size.width / 2,
|
||||
y: centerYPosition * self.emptyProposeViewCenterYFactor
|
||||
)
|
||||
|
||||
if self.isShowKeyboard {
|
||||
self.emptyProposeView?.center = CGPoint.init(
|
||||
x: size.width / 2,
|
||||
y: centerYPosition
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func addHideButton(title: String) {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem.init(
|
||||
title: title,
|
||||
style: UIBarButtonItem.Style.done,
|
||||
target: self,
|
||||
action: #selector(self.dismiss(sender:))
|
||||
)
|
||||
}
|
||||
|
||||
@objc func dismiss(sender: Any) {
|
||||
self.dismiss()
|
||||
}
|
||||
|
||||
//MARK: - keyboard
|
||||
var isShowKeyboard: Bool = false
|
||||
var keyboardSize: CGSize? = nil
|
||||
|
||||
func keyboardWillShow(duration: TimeInterval) {
|
||||
SPAnimation.animate(duration, animations: {
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
})
|
||||
}
|
||||
|
||||
func keyboardDidShow(duration: TimeInterval) {
|
||||
SPAnimation.animate(duration, animations: {
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
})
|
||||
}
|
||||
|
||||
func keyboardWillHide(duration: TimeInterval) {
|
||||
SPAnimation.animate(duration, animations: {
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
})
|
||||
}
|
||||
|
||||
func keyboardDidHide(duration: TimeInterval) {
|
||||
SPAnimation.animate(duration, animations: {
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
})
|
||||
}
|
||||
|
||||
@objc func keyboardWillShow(notification: NSNotification) {
|
||||
self.isShowKeyboard = true
|
||||
self.keyboardSize = self.keyboardSize(from: notification)
|
||||
let duration = self.keyboardAnimateDuration(from: notification) ?? 0.3
|
||||
self.keyboardWillShow(duration: duration)
|
||||
}
|
||||
|
||||
@objc func keyboardDidShow(notification: NSNotification) {
|
||||
self.isShowKeyboard = true
|
||||
self.keyboardSize = self.keyboardSize(from: notification)
|
||||
let duration = self.keyboardAnimateDuration(from: notification) ?? 0.3
|
||||
self.keyboardDidShow(duration: duration)
|
||||
}
|
||||
|
||||
@objc func keyboardWillHide(notification: NSNotification) {
|
||||
self.isShowKeyboard = false
|
||||
self.keyboardSize = nil
|
||||
let duration = self.keyboardAnimateDuration(from: notification) ?? 0.3
|
||||
self.keyboardWillHide(duration: duration)
|
||||
}
|
||||
|
||||
@objc func keyboardDidHide(notification: NSNotification) {
|
||||
self.isShowKeyboard = false
|
||||
self.keyboardSize = nil
|
||||
let duration = self.keyboardAnimateDuration(from: notification) ?? 0.3
|
||||
self.keyboardDidHide(duration: duration)
|
||||
}
|
||||
|
||||
func keyboardSize(from notification: NSNotification) -> CGSize? {
|
||||
let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue
|
||||
return keyboardFrame?.cgRectValue.size
|
||||
}
|
||||
|
||||
func keyboardAnimateDuration(from notification: NSNotification) -> TimeInterval? {
|
||||
return notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval
|
||||
}
|
||||
|
||||
//MARK: - Search
|
||||
@available(iOS 11.0, *)
|
||||
func addSearchController(placeholder: String, resultController: UIViewController?, searchResultsUpdater: UISearchResultsUpdating?, searchBarDelegate: UISearchBarDelegate?) -> UISearchController {
|
||||
|
||||
let searchController = UISearchController(searchResultsController: resultController)
|
||||
searchController.searchBar.placeholder = placeholder
|
||||
searchController.searchResultsUpdater = searchResultsUpdater
|
||||
searchController.searchBar.delegate = searchBarDelegate
|
||||
navigationItem.hidesSearchBarWhenScrolling = false
|
||||
navigationItem.searchController = searchController
|
||||
definesPresentationContext = true
|
||||
|
||||
return searchController
|
||||
}
|
||||
|
||||
//MARK: - Empty propose view
|
||||
var emptyProposeViewCenterYFactor: CGFloat = 0.94
|
||||
var emptyProposeViewWidthFactor: CGFloat = 0.7
|
||||
var emptyProposeViewHeightFactor: CGFloat = 1
|
||||
private var emptyProposeView: UIView? {
|
||||
didSet {
|
||||
if self.emptyProposeView != nil {
|
||||
self.view.addSubview(self.emptyProposeView!)
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setEmptyProposeView(_ view: UIView) {
|
||||
self.emptyProposeView = view
|
||||
}
|
||||
|
||||
func hideEmptyProposeView(animated: Bool) {
|
||||
let hideFunc = {
|
||||
self.emptyProposeView?.isHidden = true
|
||||
self.emptyProposeView?.alpha = 0
|
||||
}
|
||||
|
||||
if animated {
|
||||
SPAnimation.animate(0.3, animations: {
|
||||
self.emptyProposeView?.alpha = 0
|
||||
}, withComplection: {
|
||||
hideFunc()
|
||||
})
|
||||
} else {
|
||||
hideFunc()
|
||||
}
|
||||
}
|
||||
|
||||
func showEmptyProposeView(animated: Bool) {
|
||||
|
||||
self.emptyProposeView?.isHidden = false
|
||||
|
||||
if animated {
|
||||
SPAnimation.animate(0.3, animations: {
|
||||
self.emptyProposeView?.alpha = 1
|
||||
})
|
||||
} else {
|
||||
self.emptyProposeView?.alpha = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
+420
@@ -0,0 +1,420 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPConfirmActionViewController: UIViewController {
|
||||
|
||||
let navigationBarView = SPConfirmActionNavigationBar.init()
|
||||
let cellsView = SPConfirmActionCellsView.init()
|
||||
let confirmActionView = SPConfirmActionButtonView.init()
|
||||
|
||||
var confirmedAction: (_ controller: SPConfirmActionViewController)->() = { controller in
|
||||
controller.hide()
|
||||
}
|
||||
|
||||
private var animationDuration: TimeInterval {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.modalPresentationStyle = .overCurrentContext
|
||||
self.modalTransitionStyle = .crossDissolve
|
||||
|
||||
self.view.backgroundColor = UIColor.black.withAlphaComponent(0)
|
||||
|
||||
self.confirmActionView.button.addTarget(self, action: #selector(self.tapConfirmButton), for: .touchUpInside)
|
||||
self.navigationBarView.button.addTarget(self, action: #selector(self.hide), for: .touchUpInside)
|
||||
|
||||
self.view.addSubview(self.navigationBarView)
|
||||
self.view.addSubview(self.cellsView)
|
||||
self.view.addSubview(self.confirmActionView)
|
||||
self.updateLayout(size: self.view.frame.size)
|
||||
}
|
||||
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
self.updateLayout(size: size)
|
||||
}
|
||||
|
||||
override func viewSafeAreaInsetsDidChange() {
|
||||
if #available(iOS 11.0, *) {
|
||||
super.viewSafeAreaInsetsDidChange()
|
||||
self.updateLayout(size: self.view.frame.size)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateLayout(size: CGSize) {
|
||||
self.confirmActionView.setWidth(size.width)
|
||||
self.confirmActionView.sizeToFit()
|
||||
self.confirmActionView.frame.origin.x = 0
|
||||
self.confirmActionView.frame.origin.y = size.height - self.confirmActionView.frame.height
|
||||
|
||||
self.cellsView.setWidth(size.width)
|
||||
self.cellsView.sizeToFit()
|
||||
self.cellsView.frame.origin.x = 0
|
||||
self.cellsView.frame.origin.y = self.confirmActionView.frame.origin.y - self.cellsView.frame.height
|
||||
|
||||
self.navigationBarView.setWidth(size.width)
|
||||
self.navigationBarView.sizeToFit()
|
||||
self.navigationBarView.frame.origin.x = 0
|
||||
self.navigationBarView.frame.origin.y = self.cellsView.frame.origin.y - self.navigationBarView.frame.height
|
||||
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
self.confirmActionView.frame.origin.y = self.view.frame.size.height
|
||||
self.cellsView.frame.origin.y = self.view.frame.size.height
|
||||
self.navigationBarView.frame.origin.y = self.view.frame.size.height
|
||||
|
||||
SPAnimationSpring.animate(self.animationDuration, animations: {
|
||||
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.4)
|
||||
|
||||
self.confirmActionView.frame.origin.y = self.view.frame.height - self.confirmActionView.frame.height
|
||||
self.cellsView.frame.origin.y = self.confirmActionView.frame.origin.y - self.cellsView.frame.height
|
||||
self.navigationBarView.frame.origin.y = self.cellsView.frame.origin.y - self.navigationBarView.frame.height
|
||||
}, spring: 1,
|
||||
velocity: 1,
|
||||
options: .transitionCurlUp)
|
||||
}
|
||||
|
||||
func present(on viewController: UIViewController) {
|
||||
self.modalPresentationStyle = .overCurrentContext
|
||||
self.modalTransitionStyle = .crossDissolve
|
||||
viewController.present(self, animated: false, completion: nil)
|
||||
}
|
||||
|
||||
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
SPAnimationSpring.animate(self.animationDuration, animations: {
|
||||
self.view.backgroundColor = UIColor.black.withAlphaComponent(0)
|
||||
self.confirmActionView.frame.origin.y = self.view.frame.size.height
|
||||
self.cellsView.frame.origin.y = self.view.frame.size.height
|
||||
self.navigationBarView.frame.origin.y = self.view.frame.size.height
|
||||
}, spring: 1,
|
||||
velocity: 1,
|
||||
options: .transitionCurlDown,
|
||||
withComplection: {
|
||||
super.dismiss(animated: false) {
|
||||
completion?()
|
||||
}})
|
||||
}
|
||||
|
||||
func addCell(task: String, title: String, subtitle: String? = nil, imageLink: String? = nil, image: UIImage? = nil) {
|
||||
let cell = SPConfirmActionCellView.init(
|
||||
task: task,
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
imageLink: imageLink,
|
||||
image: image
|
||||
)
|
||||
self.cellsView.addSubview(cell)
|
||||
}
|
||||
|
||||
@objc func hide() {
|
||||
self.dismiss(animated: true)
|
||||
}
|
||||
|
||||
@objc func tapConfirmButton() {
|
||||
self.confirmedAction(self)
|
||||
}
|
||||
|
||||
class SPConfirmActionNavigationBar: UIView {
|
||||
|
||||
let label: UILabel = UILabel.init()
|
||||
let button: UIButton = UIButton.init()
|
||||
let separatorView = UIView.init()
|
||||
let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: UIBlurEffect.Style.extraLight))
|
||||
|
||||
private var leftAndRightSpace: CGFloat {
|
||||
return 18
|
||||
}
|
||||
|
||||
init() {
|
||||
super.init(frame: CGRect.zero)
|
||||
|
||||
self.addSubview(self.backgroundView)
|
||||
|
||||
self.separatorView.backgroundColor = UIColor.black.withAlphaComponent(0.15)
|
||||
self.addSubview(self.separatorView)
|
||||
|
||||
self.label.numberOfLines = 1
|
||||
self.label.textColor = UIColor.black
|
||||
self.label.text = "SPConfirmAction"
|
||||
self.label.font = UIFont.system(type: .DemiBold, size: 16)
|
||||
self.addSubview(self.label)
|
||||
|
||||
self.button.setTitle("Сancel", for: .normal)
|
||||
self.button.setTitleColor(SPNativeStyleKit.Colors.blue)
|
||||
self.button.titleLabel?.font = UIFont.system(type: .DemiBold, size: 16)
|
||||
self.addSubview(self.button)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func sizeToFit() {
|
||||
super.sizeToFit()
|
||||
self.setHeight(45)
|
||||
self.layoutSubviews()
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.backgroundView.setEqualsBoundsFromSuperview()
|
||||
|
||||
self.label.sizeToFit()
|
||||
self.label.frame.origin.x = self.leftAndRightSpace
|
||||
self.label.center.y = self.frame.height / 2
|
||||
|
||||
self.button.sizeToFit()
|
||||
self.button.frame.origin.x = self.frame.width - self.leftAndRightSpace - self.button.frame.width
|
||||
self.button.center.y = self.frame.height / 2
|
||||
|
||||
self.separatorView.frame = CGRect.init(x: 0, y: self.frame.height - 1, width: self.frame.width, height: 1)
|
||||
}
|
||||
}
|
||||
|
||||
class SPConfirmActionCellView: UIView {
|
||||
|
||||
var taskLabel = UILabel.init()
|
||||
var titleLabel: UILabel = UILabel.init()
|
||||
var subtitleLabel: UILabel?
|
||||
var imageView: SPDownloadingImageView?
|
||||
var separatorView = UIView.init()
|
||||
|
||||
var yLeftPosition: CGFloat = 0
|
||||
var baseSpace: CGFloat = 0
|
||||
|
||||
init(task: String, title: String, subtitle: String? = nil, imageLink: String? = nil, image: UIImage? = nil) {
|
||||
super.init(frame: CGRect.zero)
|
||||
|
||||
self.separatorView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.3)
|
||||
self.addSubview(self.separatorView)
|
||||
|
||||
self.taskLabel.text = task.uppercased()
|
||||
self.taskLabel.font = UIFont.system(type: .Medium, size: 13)
|
||||
self.taskLabel.textColor = SPNativeStyleKit.Colors.gray
|
||||
self.taskLabel.numberOfLines = 1
|
||||
self.taskLabel.textAlignment = .right
|
||||
self.addSubview(self.taskLabel)
|
||||
|
||||
self.titleLabel.text = title.uppercased()
|
||||
self.titleLabel.font = UIFont.system(type: .Medium, size: 13)
|
||||
self.titleLabel.textColor = UIColor.black
|
||||
self.titleLabel.numberOfLines = 1
|
||||
self.titleLabel.textAlignment = .left
|
||||
self.addSubview(self.titleLabel)
|
||||
|
||||
if subtitle != nil {
|
||||
self.subtitleLabel = UILabel.init()
|
||||
self.subtitleLabel?.text = subtitle?.uppercased()
|
||||
self.subtitleLabel?.font = UIFont.system(type: .Medium, size: 13)
|
||||
self.subtitleLabel?.textColor = SPNativeStyleKit.Colors.gray
|
||||
self.subtitleLabel?.numberOfLines = 0
|
||||
self.subtitleLabel?.textAlignment = .left
|
||||
self.addSubview(self.subtitleLabel!)
|
||||
}
|
||||
if imageLink != nil {
|
||||
self.imageView = SPDownloadingImageView.init()
|
||||
self.imageView?.setImage(link: imageLink!)
|
||||
self.imageView?.layer.cornerRadius = 12
|
||||
self.addSubview(self.imageView!)
|
||||
}
|
||||
if image != nil {
|
||||
self.imageView = SPDownloadingImageView.init()
|
||||
self.imageView?.setImage(image: image!, animatable: true)
|
||||
self.imageView?.layer.cornerRadius = 12
|
||||
self.addSubview(self.imageView!)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func sizeToFit() {
|
||||
super.sizeToFit()
|
||||
self.setHeight(45)
|
||||
if self.imageView != nil {
|
||||
self.setHeight(75)
|
||||
}
|
||||
if self.subtitleLabel != nil {
|
||||
var height = 45 + self.subtitleLabel!.frame.height
|
||||
height.setIfFewer(when: 75)
|
||||
self.setHeight(height)
|
||||
}
|
||||
self.layoutSubviews()
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.taskLabel.sizeToFit()
|
||||
self.taskLabel.center.y = self.frame.height / 2
|
||||
self.taskLabel.frame.origin.x = self.yLeftPosition - self.taskLabel.frame.width
|
||||
|
||||
self.titleLabel.sizeToFit()
|
||||
self.titleLabel.center.y = self.frame.height / 2
|
||||
self.titleLabel.frame.origin.x = self.yLeftPosition + self.baseSpace
|
||||
self.titleLabel.setWidth(self.frame.width - self.titleLabel.frame.origin.x - self.baseSpace)
|
||||
|
||||
if subtitleLabel != nil {
|
||||
let allContentHeight: CGFloat = self.titleLabel.frame.height + self.subtitleLabel!.frame.height + 3
|
||||
self.titleLabel.frame.origin.y = (self.frame.height - allContentHeight) / 2
|
||||
self.subtitleLabel?.frame.origin.x = self.titleLabel.frame.origin.x
|
||||
self.subtitleLabel?.frame.origin.y = self.titleLabel.frame.bottomYPosition + 3
|
||||
self.subtitleLabel?.sizeToFit()
|
||||
self.subtitleLabel?.setWidth(self.titleLabel.frame.width)
|
||||
|
||||
}
|
||||
|
||||
if imageView != nil {
|
||||
self.taskLabel.frame = CGRect.zero
|
||||
var imageSideSize = self.frame.height - 10 * 2
|
||||
imageSideSize.setIfMore(when: 40)
|
||||
self.imageView?.frame = CGRect.init(x: self.yLeftPosition - imageSideSize, y: 0, width: imageSideSize, height: imageSideSize)
|
||||
self.imageView?.center.y = self.frame.height / 2
|
||||
}
|
||||
|
||||
|
||||
self.separatorView.frame = CGRect.init(x: 18, y: self.frame.height - 1, width: self.frame.width - 18, height: 1)
|
||||
}
|
||||
}
|
||||
|
||||
class SPConfirmActionCellsView: UIView {
|
||||
|
||||
let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: UIBlurEffect.Style.extraLight))
|
||||
|
||||
private var baseSpace: CGFloat = 18
|
||||
|
||||
init() {
|
||||
super.init(frame: CGRect.zero)
|
||||
self.addSubview(self.backgroundView)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.backgroundView.setEqualsBoundsFromSuperview()
|
||||
|
||||
var maxLabelWidth: CGFloat = 0
|
||||
for view in self.subviews {
|
||||
if view != self.backgroundView {
|
||||
if let cellView = view as? SPConfirmActionCellView {
|
||||
cellView.sizeToFit()
|
||||
if maxLabelWidth < cellView.taskLabel.frame.width {
|
||||
maxLabelWidth = cellView.taskLabel.frame.width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for view in self.subviews {
|
||||
if view != self.backgroundView {
|
||||
if let cellView = view as? SPConfirmActionCellView {
|
||||
cellView.yLeftPosition = self.baseSpace * 2 + maxLabelWidth
|
||||
cellView.baseSpace = self.baseSpace
|
||||
cellView.layoutSubviews()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var yPosition: CGFloat = 0
|
||||
|
||||
for view in subviews {
|
||||
if view != self.backgroundView {
|
||||
view.sizeToFit()
|
||||
view.setWidth(self.frame.width)
|
||||
view.frame.origin = CGPoint.init(x: 0, y: yPosition)
|
||||
yPosition += view.frame.height
|
||||
}
|
||||
}
|
||||
self.setHeight(yPosition)
|
||||
}
|
||||
|
||||
override func sizeToFit() {
|
||||
super.sizeToFit()
|
||||
|
||||
var height: CGFloat = 0
|
||||
for view in subviews {
|
||||
if view != self.backgroundView {
|
||||
view.sizeToFit()
|
||||
height += view.frame.height
|
||||
}
|
||||
}
|
||||
self.setHeight(height)
|
||||
self.layoutSubviews()
|
||||
}
|
||||
}
|
||||
|
||||
class SPConfirmActionButtonView: UIView {
|
||||
|
||||
let button = SPAppStoreActionButton()
|
||||
let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: UIBlurEffect.Style.extraLight))
|
||||
|
||||
init() {
|
||||
super.init(frame: CGRect.zero)
|
||||
self.button.style = .buyInStore
|
||||
self.button.setTitle("Execute", for: .normal)
|
||||
self.button.titleLabel?.font = UIFont.system(type: .DemiBold, size: 16)
|
||||
self.addSubview(self.backgroundView)
|
||||
self.addSubview(self.button)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func sizeToFit() {
|
||||
super.sizeToFit()
|
||||
var baseHeight: CGFloat = 80
|
||||
if #available(iOS 11.0, *) {
|
||||
baseHeight += (self.superview?.safeAreaInsets.bottom ?? 0)
|
||||
}
|
||||
self.setHeight(baseHeight)
|
||||
self.layoutSubviews()
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.backgroundView.setEqualsBoundsFromSuperview()
|
||||
self.button.sizeToFit()
|
||||
self.button.setXCenteringFromSuperview()
|
||||
|
||||
var safeAreaInsetsBottom: CGFloat = 0
|
||||
if #available(iOS 11.0, *) {
|
||||
safeAreaInsetsBottom = (self.superview?.safeAreaInsets.bottom ?? 0)
|
||||
}
|
||||
self.button.center.y = (self.frame.height - safeAreaInsetsBottom) / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+258
@@ -0,0 +1,258 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPNativeTableViewController: SPBaseTableViewController {
|
||||
|
||||
let labelTableViewCellIdentifier: String = "labelTableViewCellIdentifier"
|
||||
let textFieldTableViewCellIdentifier: String = "textFieldTableViewCellIdentifier"
|
||||
let buttonTableViewCellIdentifier: String = "buttonTableViewCellIdentifier"
|
||||
let textTableViewCellIdentifier: String = "textTableViewCellIdentifier"
|
||||
let textInputTableViewCellIdentifier: String = "textInputTableViewCellIdentifier"
|
||||
let promoTableViewCellIdentifier: String = "promoTableViewCellIdentifier"
|
||||
let featuredTitleTableViewCellIdentifier: String = "featuredTitleTableViewCellIdentifier"
|
||||
let mailTableViewCellIdentifier: String = "mailTableViewCellIdentifier"
|
||||
let collectionImagesTableViewCellIdentifier: String = "collectionImagesTableViewCellIdentifier"
|
||||
let imageTableViewCellIdentifier: String = "imageTableViewCellIdentifier"
|
||||
let proposeTableViewCellIdentifier: String = "proposeTableViewCellIdentifier"
|
||||
let mengTransformTableViewCell = "mengTransformTableViewCell"
|
||||
|
||||
var showTopInsets: Bool = true
|
||||
var showBottomInsets: Bool = true
|
||||
var autoTopSpace: Bool = true
|
||||
var autoBottomSpace: Bool = true
|
||||
private var autoSpaceHeight: CGFloat = 35
|
||||
|
||||
init() {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.statusBar = .dark
|
||||
|
||||
self.tableView = UITableView.init(frame: self.view.bounds, style: UITableView.Style.grouped)
|
||||
self.setPrefersLargeNavigationTitle("Title")
|
||||
|
||||
if #available(iOS 11.0, *) {
|
||||
self.tableView.contentInsetAdjustmentBehavior = .always
|
||||
}
|
||||
|
||||
self.tableView.backgroundColor = SPNativeStyleKit.Colors.customGray
|
||||
self.tableView.delaysContentTouches = false
|
||||
self.tableView.allowsSelection = false
|
||||
self.tableView.rowHeight = UITableView.automaticDimension
|
||||
self.tableView.sectionFooterHeight = UITableView.automaticDimension
|
||||
self.tableView.sectionHeaderHeight = UITableView.automaticDimension
|
||||
self.tableView.estimatedRowHeight = 44
|
||||
|
||||
self.tableView.register(SPFormTextFiledTableViewCell.self, forCellReuseIdentifier: self.textFieldTableViewCellIdentifier)
|
||||
self.tableView.register(SPFormLabelTableViewCell.self, forCellReuseIdentifier: self.labelTableViewCellIdentifier)
|
||||
self.tableView.register(SPFormButtonTableViewCell.self, forCellReuseIdentifier: self.buttonTableViewCellIdentifier)
|
||||
self.tableView.register(SPFormTextTableViewCell.self, forCellReuseIdentifier: self.textTableViewCellIdentifier)
|
||||
self.tableView.register(SPFormTextInputTableViewCell.self, forCellReuseIdentifier: self.textInputTableViewCellIdentifier)
|
||||
self.tableView.register(SPPromoTableViewCell.self, forCellReuseIdentifier: self.promoTableViewCellIdentifier)
|
||||
self.tableView.register(SPFormFeaturedTitleTableViewCell.self, forCellReuseIdentifier: self.featuredTitleTableViewCellIdentifier)
|
||||
self.tableView.register(SPFormMailTableViewCell.self, forCellReuseIdentifier: self.mailTableViewCellIdentifier)
|
||||
self.tableView.register(SPCollectionImagesTableViewCell.self, forCellReuseIdentifier: self.collectionImagesTableViewCellIdentifier)
|
||||
self.tableView.register(SPImageTableViewCell.self, forCellReuseIdentifier: self.imageTableViewCellIdentifier)
|
||||
self.tableView.register(SPProposeTableViewCell.self, forCellReuseIdentifier: self.proposeTableViewCellIdentifier)
|
||||
self.tableView.register(SPMengTransformTableViewCell.self, forCellReuseIdentifier: self.mengTransformTableViewCell)
|
||||
|
||||
self.activityIndicatorView.stopAnimating()
|
||||
self.activityIndicatorView.color = SPNativeStyleKit.Colors.gray
|
||||
self.view.addSubview(self.activityIndicatorView)
|
||||
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
}
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return super.numberOfSections(in: tableView)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return super.tableView(tableView, numberOfRowsInSection: section)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
fatalError("SPNativeTableViewController - need ivveride cellForRowAt")
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
return nil
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func dequeueTextFiledTableViewCell(indexPath: IndexPath) -> SPFormTextFiledTableViewCell {
|
||||
return tableView.dequeueReusableCell(withIdentifier: self.textFieldTableViewCellIdentifier, for: indexPath as IndexPath) as! SPFormTextFiledTableViewCell
|
||||
}
|
||||
|
||||
func dequeueLabelTableViewCell(indexPath: IndexPath) -> SPFormLabelTableViewCell {
|
||||
return tableView.dequeueReusableCell(withIdentifier: self.labelTableViewCellIdentifier, for: indexPath as IndexPath) as! SPFormLabelTableViewCell
|
||||
}
|
||||
|
||||
func dequeueButtonTableViewCell(indexPath: IndexPath) -> SPFormButtonTableViewCell {
|
||||
return tableView.dequeueReusableCell(withIdentifier: self.buttonTableViewCellIdentifier, for: indexPath as IndexPath) as! SPFormButtonTableViewCell
|
||||
}
|
||||
|
||||
func dequeueTextTableViewCell(indexPath: IndexPath) -> SPFormTextTableViewCell {
|
||||
return tableView.dequeueReusableCell(withIdentifier: self.textTableViewCellIdentifier, for: indexPath as IndexPath) as! SPFormTextTableViewCell
|
||||
}
|
||||
|
||||
func dequeueTextInputTableViewCell(indexPath: IndexPath) -> SPFormTextInputTableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: self.textInputTableViewCellIdentifier, for: indexPath as IndexPath) as! SPFormTextInputTableViewCell
|
||||
cell.currentIndexPath = indexPath
|
||||
return cell
|
||||
}
|
||||
|
||||
func dequeuePromoTableViewCell(indexPath: IndexPath? = nil) -> SPPromoTableViewCell {
|
||||
if indexPath == nil {
|
||||
return tableView.dequeueReusableCell(withIdentifier: self.promoTableViewCellIdentifier) as! SPPromoTableViewCell
|
||||
} else {
|
||||
return tableView.dequeueReusableCell(withIdentifier: self.promoTableViewCellIdentifier, for: indexPath! as IndexPath) as! SPPromoTableViewCell
|
||||
}
|
||||
}
|
||||
|
||||
func dequeueFeaturedTitleTableViewCell(indexPath: IndexPath) -> SPFormFeaturedTitleTableViewCell {
|
||||
return tableView.dequeueReusableCell(withIdentifier: self.featuredTitleTableViewCellIdentifier, for: indexPath as IndexPath) as! SPFormFeaturedTitleTableViewCell
|
||||
}
|
||||
|
||||
func dequeueMailTableViewCell(indexPath: IndexPath) -> SPFormMailTableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: self.mailTableViewCellIdentifier, for: indexPath as IndexPath) as! SPFormMailTableViewCell
|
||||
cell.currentIndexPath = indexPath
|
||||
return cell
|
||||
}
|
||||
|
||||
func dequeueCollectionImagesTableViewCell(indexPath: IndexPath) -> SPCollectionImagesTableViewCell {
|
||||
return tableView.dequeueReusableCell(withIdentifier: self.collectionImagesTableViewCellIdentifier, for: indexPath as IndexPath) as! SPCollectionImagesTableViewCell
|
||||
}
|
||||
|
||||
func dequeueImageTableViewCell(indexPath: IndexPath) -> SPImageTableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: self.imageTableViewCellIdentifier, for: indexPath as IndexPath) as! SPImageTableViewCell
|
||||
cell.currentIndexPath = indexPath
|
||||
return cell
|
||||
}
|
||||
|
||||
func dequeueProposeTableViewCell(indexPath: IndexPath) -> SPProposeTableViewCell {
|
||||
return tableView.dequeueReusableCell(withIdentifier: self.proposeTableViewCellIdentifier, for: indexPath as IndexPath) as! SPProposeTableViewCell
|
||||
}
|
||||
|
||||
func dequeueMengTransformTableViewCell(indexPath: IndexPath) -> SPMengTransformTableViewCell {
|
||||
return tableView.dequeueReusableCell(withIdentifier: self.mengTransformTableViewCell, for: indexPath as IndexPath) as! SPMengTransformTableViewCell
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - manage selection
|
||||
extension SPNativeTableViewController {
|
||||
|
||||
override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
|
||||
|
||||
if let _ = tableView.cellForRow(at: indexPath) as? SPFormFeaturedTitleTableViewCell {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//MARK: - manage spaces
|
||||
extension SPNativeTableViewController {
|
||||
|
||||
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
if section == 0 {
|
||||
return self.showTopInsets ? super.tableView(tableView, viewForHeaderInSection: section) : nil
|
||||
} else {
|
||||
return super.tableView(tableView, viewForHeaderInSection: section)
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
|
||||
if section == self.tableView.lastSection {
|
||||
return self.showBottomInsets ? super.tableView(tableView, viewForFooterInSection: section) : nil
|
||||
} else {
|
||||
return super.tableView(tableView, viewForFooterInSection: section)
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
let firstSection = self.tableView.firstSectionWithRows
|
||||
if section == firstSection {
|
||||
if self.showTopInsets {
|
||||
if self.autoTopSpace {
|
||||
if self.tableView(self.tableView, viewForHeaderInSection: firstSection!) != nil {
|
||||
return UITableView.automaticDimension
|
||||
}
|
||||
if self.tableView(self.tableView, titleForHeaderInSection: firstSection!) != nil {
|
||||
return UITableView.automaticDimension
|
||||
}
|
||||
return self.autoSpaceHeight
|
||||
} else {
|
||||
return UITableView.automaticDimension
|
||||
}
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
} else {
|
||||
if self.tableView.numberOfRows(inSection: section) == 0 {
|
||||
return 0
|
||||
} else {
|
||||
return UITableView.automaticDimension
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
|
||||
if section == self.tableView.lastSectionWithRows {
|
||||
if self.showBottomInsets {
|
||||
if self.autoBottomSpace {
|
||||
if self.tableView(self.tableView, viewForFooterInSection: self.tableView.lastSectionWithRows!) != nil {
|
||||
return UITableView.automaticDimension
|
||||
}
|
||||
if self.tableView(self.tableView, titleForFooterInSection: self.tableView.lastSectionWithRows!) != nil {
|
||||
return UITableView.automaticDimension
|
||||
}
|
||||
return self.autoSpaceHeight
|
||||
} else {
|
||||
return UITableView.automaticDimension
|
||||
}
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
} else {
|
||||
if self.tableView.numberOfRows(inSection: section) == 0 {
|
||||
return 0
|
||||
} else {
|
||||
return UITableView.automaticDimension
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+282
@@ -0,0 +1,282 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPProposeViewController: SPBaseViewController {
|
||||
|
||||
private let data: Data
|
||||
internal let areaView = AreaView()
|
||||
|
||||
private var animationDuration: TimeInterval {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
private var space: CGFloat {
|
||||
return 6
|
||||
}
|
||||
|
||||
init(title: String, subtitle: String, buttonTitle: String, imageLink: String? = nil, image: UIImage? = nil, complection: @escaping (_ isConfirmed: Bool)->() = {_ in }) {
|
||||
self.data = Data(
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
buttonTitle: buttonTitle,
|
||||
imageLink: imageLink,
|
||||
image: image,
|
||||
complection: complection
|
||||
)
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
self.modalPresentationStyle = .overCurrentContext
|
||||
self.view.backgroundColor = UIColor.black.withAlphaComponent(0)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.areaView.isHidden = true
|
||||
self.areaView.titleLabel.text = self.data.title
|
||||
self.areaView.subtitleLabel.text = self.data.subtitle
|
||||
self.areaView.button.setTitle(self.data.buttonTitle, for: UIControl.State.normal)
|
||||
|
||||
self.areaView.button.addTarget(self, action: #selector(self.open), for: UIControl.Event.touchUpInside)
|
||||
self.areaView.closeButton.addTarget(self, action: #selector(self.close), for: UIControl.Event.touchUpInside)
|
||||
|
||||
self.view.addSubview(self.areaView)
|
||||
|
||||
let panGesture = UIPanGestureRecognizer.init(target: self, action: #selector(self.handleGesture(sender:)))
|
||||
panGesture.maximumNumberOfTouches = 1
|
||||
self.areaView.addGestureRecognizer(panGesture)
|
||||
|
||||
self.areaView.imageView.setParalax(amount: 0.1)
|
||||
if let image = self.data.image {
|
||||
self.areaView.imageView.setImage(image: image, animatable: false)
|
||||
}
|
||||
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
}
|
||||
|
||||
override func updateLayout(with size: CGSize) {
|
||||
self.areaView.setWidth(size.width - (self.space * 2))
|
||||
self.areaView.layoutSubviews()
|
||||
self.areaView.sizeToFit()
|
||||
self.areaView.frame.origin.x = self.space
|
||||
self.areaView.frame.origin.y = self.view.frame.size.height - self.areaView.frame.height - (self.bottomSafeArea / 2) - self.space
|
||||
|
||||
}
|
||||
|
||||
func present(on viewController: UIViewController) {
|
||||
viewController.present(self, animated: false, completion: {
|
||||
SPVibration.impact(SPVibration.Mode.warning)
|
||||
self.areaView.frame.origin.y = self.view.frame.size.height
|
||||
self.areaView.isHidden = false
|
||||
SPAnimationSpring.animate(self.animationDuration, animations: {
|
||||
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.6)
|
||||
|
||||
self.areaView.frame.origin.y = self.view.frame.size.height - self.areaView.frame.height - (self.bottomSafeArea / 2) - self.space
|
||||
}, spring: 1,
|
||||
velocity: 1,
|
||||
options: .transitionCurlUp)
|
||||
})
|
||||
}
|
||||
|
||||
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
|
||||
let hide = {
|
||||
self.view.backgroundColor = UIColor.black.withAlphaComponent(0)
|
||||
self.areaView.frame.origin.y = self.view.frame.size.height
|
||||
}
|
||||
|
||||
let dismiss = {
|
||||
super.dismiss(animated: false) {
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
|
||||
if flag {
|
||||
SPAnimationSpring.animate(self.animationDuration, animations: {
|
||||
hide()
|
||||
}, spring: 1,
|
||||
velocity: 1,
|
||||
options: .transitionCurlDown,
|
||||
withComplection: {
|
||||
dismiss()
|
||||
})
|
||||
} else {
|
||||
hide()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func handleGesture(sender: UIPanGestureRecognizer) {
|
||||
|
||||
let returnAreaViewToPoint = {
|
||||
SPAnimationSpring.animate(self.animationDuration, animations: {
|
||||
self.areaView.frame.origin.y = self.view.frame.size.height - self.areaView.frame.height - (self.bottomSafeArea / 2) - self.space
|
||||
}, spring: 1,
|
||||
velocity: 1,
|
||||
options: .transitionCurlDown,
|
||||
withComplection: {
|
||||
})
|
||||
}
|
||||
|
||||
switch sender.state {
|
||||
case .began:
|
||||
break
|
||||
case .cancelled:
|
||||
returnAreaViewToPoint()
|
||||
case .changed:
|
||||
let translation = sender.translation(in: self.view)
|
||||
self.areaView.center = CGPoint(x: areaView.center.x + 0, y: areaView.center.y + translation.y / 4)
|
||||
sender.setTranslation(CGPoint.zero, in: self.view)
|
||||
case .ended:
|
||||
returnAreaViewToPoint()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@objc func open() {
|
||||
self.data.complection(true)
|
||||
self.dismiss(animated: true)
|
||||
}
|
||||
|
||||
@objc func close() {
|
||||
self.data.complection(false)
|
||||
self.dismiss(animated: true)
|
||||
}
|
||||
|
||||
class AreaView: UIView {
|
||||
|
||||
let titleLabel = UILabel()
|
||||
let subtitleLabel = UILabel()
|
||||
let imageView = SPDownloadingImageView()
|
||||
let button = SPNativeLargeButton()
|
||||
let closeButton = SPSystemIconButton(type: SPSystemIconType.close)
|
||||
|
||||
var imageSideSize: CGFloat = 160
|
||||
|
||||
private let space: CGFloat = 36
|
||||
|
||||
init() {
|
||||
super.init(frame: CGRect.zero)
|
||||
self.backgroundColor = UIColor.white
|
||||
self.layer.masksToBounds = true
|
||||
self.layer.cornerRadius = 34
|
||||
|
||||
self.titleLabel.font = UIFont.system(type: .Regular, size: 28)
|
||||
self.titleLabel.textColor = UIColor.init(hex: "939393")
|
||||
self.titleLabel.numberOfLines = 1
|
||||
self.titleLabel.adjustsFontSizeToFitWidth = true
|
||||
self.titleLabel.minimumScaleFactor = 0.5
|
||||
self.titleLabel.setCenteringAlignment()
|
||||
self.addSubview(self.titleLabel)
|
||||
|
||||
self.subtitleLabel.font = UIFont.system(type: .Regular, size: 16)
|
||||
self.subtitleLabel.textColor = SPNativeStyleKit.Colors.black
|
||||
self.subtitleLabel.numberOfLines = 0
|
||||
self.subtitleLabel.setCenteringAlignment()
|
||||
self.addSubview(self.subtitleLabel)
|
||||
|
||||
self.imageView.gradeView.backgroundColor = UIColor.white
|
||||
self.imageView.contentMode = .scaleAspectFit
|
||||
self.imageView.layer.masksToBounds = true
|
||||
self.addSubview(self.imageView)
|
||||
|
||||
self.button.titleLabel?.font = UIFont.system(type: UIFont.BoldType.Medium, size: 15)
|
||||
self.button.setTitleColor(SPNativeStyleKit.Colors.black)
|
||||
self.button.backgroundColor = UIColor.init(hex: "D4D3DB")
|
||||
self.addSubview(self.button)
|
||||
|
||||
self.closeButton.widthIconFactor = 0.4
|
||||
self.closeButton.heightIconFactor = 0.4
|
||||
self.closeButton.backgroundColor = UIColor.init(hex: "EFEFF4")
|
||||
self.closeButton.color = UIColor.init(hex: "979797")
|
||||
self.addSubview(self.closeButton)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.titleLabel.sizeToFit()
|
||||
self.titleLabel.frame.origin.y = self.space * 0.8
|
||||
self.titleLabel.setWidth(self.frame.width - self.space * 3)
|
||||
self.titleLabel.setXCenteringFromSuperview()
|
||||
|
||||
self.subtitleLabel.sizeToFit()
|
||||
self.subtitleLabel.frame.origin.y = self.titleLabel.frame.bottomYPosition + 8
|
||||
self.subtitleLabel.setWidth(self.frame.width - self.space * 2)
|
||||
self.subtitleLabel.setXCenteringFromSuperview()
|
||||
|
||||
self.imageView.frame = CGRect.init(
|
||||
x: 0, y: self.subtitleLabel.frame.bottomYPosition + self.space / 2,
|
||||
width: self.imageSideSize,
|
||||
height: self.imageSideSize
|
||||
)
|
||||
self.imageView.setXCenteringFromSuperview()
|
||||
|
||||
self.button.sizeToFit()
|
||||
self.button.setWidth(self.frame.width - self.space * 2)
|
||||
self.button.frame.origin.y = self.imageView.frame.bottomYPosition + self.space / 2
|
||||
self.button.setXCenteringFromSuperview()
|
||||
|
||||
self.closeButton.frame = CGRect.init(x: 0, y: 0, width: 24, height: 24)
|
||||
self.closeButton.frame.origin.x = self.frame.width - self.closeButton.frame.width - 20
|
||||
self.closeButton.frame.origin.y = 20
|
||||
self.closeButton.round()
|
||||
}
|
||||
|
||||
override func sizeToFit() {
|
||||
super.sizeToFit()
|
||||
self.setHeight(self.button.frame.bottomYPosition + self.space)
|
||||
}
|
||||
}
|
||||
|
||||
struct Data {
|
||||
var title: String
|
||||
var subtitle: String
|
||||
var buttonTitle: String
|
||||
var imageLink: String?
|
||||
var image: UIImage?
|
||||
var complection: (_ isConfirmed: Bool)->()
|
||||
}
|
||||
|
||||
var bottomSafeArea: CGFloat {
|
||||
var bottomSafeArea: CGFloat = 0
|
||||
if let window = UIApplication.shared.keyWindow {
|
||||
if #available(iOS 11.0, *) {
|
||||
bottomSafeArea = window.safeAreaInsets.bottom
|
||||
}
|
||||
} else {
|
||||
bottomSafeArea = 0
|
||||
}
|
||||
|
||||
return bottomSafeArea
|
||||
}
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPStatusBarManagerViewController: UIViewController {
|
||||
|
||||
var statusBar: SPStatusBar = .dark {
|
||||
didSet {
|
||||
UIView.animate(withDuration: 0.3) { () -> Void in
|
||||
self.setNeedsStatusBarAppearanceUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
switch self.statusBar {
|
||||
case .dark:
|
||||
return .default
|
||||
case .light:
|
||||
return .lightContent
|
||||
}
|
||||
}
|
||||
|
||||
override public var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
|
||||
return .slide
|
||||
}
|
||||
|
||||
public var isHiddenStatusBar: Bool = false {
|
||||
didSet {
|
||||
UIView.animate(withDuration: 0.3) { () -> Void in
|
||||
self.setNeedsStatusBarAppearanceUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public var prefersStatusBarHidden: Bool {
|
||||
return isHiddenStatusBar
|
||||
}
|
||||
}
|
||||
|
||||
public class SPStatusBarManagerTableViewController: UITableViewController {
|
||||
|
||||
var statusBar: SPStatusBar = .dark {
|
||||
didSet {
|
||||
UIView.animate(withDuration: 0.3) { () -> Void in
|
||||
self.setNeedsStatusBarAppearanceUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
switch self.statusBar {
|
||||
case .dark:
|
||||
return .default
|
||||
case .light:
|
||||
return .lightContent
|
||||
}
|
||||
}
|
||||
|
||||
override public var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
|
||||
return .slide
|
||||
}
|
||||
|
||||
public var isHiddenStatusBar: Bool = false {
|
||||
didSet {
|
||||
UIView.animate(withDuration: 0.3) { () -> Void in
|
||||
self.setNeedsStatusBarAppearanceUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public var prefersStatusBarHidden: Bool {
|
||||
return isHiddenStatusBar
|
||||
}
|
||||
}
|
||||
|
||||
public class SPStatusBarManagerNavigationController: UINavigationController {
|
||||
|
||||
var statusBar: SPStatusBar = .dark {
|
||||
didSet {
|
||||
UIView.animate(withDuration: 0.3) { () -> Void in
|
||||
self.setNeedsStatusBarAppearanceUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
switch self.statusBar {
|
||||
case .dark:
|
||||
return .default
|
||||
case .light:
|
||||
return .lightContent
|
||||
}
|
||||
}
|
||||
|
||||
override public var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
|
||||
return .slide
|
||||
}
|
||||
|
||||
public var isHiddenStatusBar: Bool = false {
|
||||
didSet {
|
||||
UIView.animate(withDuration: 0.3) { () -> Void in
|
||||
self.setNeedsStatusBarAppearanceUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public var prefersStatusBarHidden: Bool {
|
||||
return isHiddenStatusBar
|
||||
}
|
||||
|
||||
public override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
UIView.animate(withDuration: 0.3) { () -> Void in
|
||||
self.setNeedsStatusBarAppearanceUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPWelcomeViewController: SPBaseViewController {
|
||||
|
||||
let imageView = SPDownloadingImageView()
|
||||
let titleLabel = UILabel()
|
||||
let subtitleLabel = UILabel()
|
||||
let descriptionLabel = UILabel()
|
||||
let commentLabel = UILabel()
|
||||
let button = SPNativeLargeButton()
|
||||
|
||||
private var data: SPWelcomeData
|
||||
private var views: [UIView] = []
|
||||
private var isPresented: Bool = false
|
||||
|
||||
static var isNeedPresent: Bool {
|
||||
return self.isFirstLaunch || self.hasBeenUpdated
|
||||
}
|
||||
|
||||
init(data: SPWelcomeData) {
|
||||
self.data = data
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
self.data = SPWelcomeData()
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.statusBar = self.data.statusBarStyle
|
||||
self.view.backgroundColor = self.data.backgroundColor
|
||||
|
||||
self.imageView.setImage(image: self.data.image, animatable: false)
|
||||
self.imageView.contentMode = .scaleAspectFit
|
||||
self.imageView.layer.cornerRadius = 6
|
||||
self.imageView.layer.masksToBounds = true
|
||||
self.view.addSubview(self.imageView)
|
||||
self.views.append(self.imageView)
|
||||
|
||||
self.titleLabel.text = self.data.title
|
||||
self.titleLabel.font = UIFont.system(type: UIFont.BoldType.Heavy, size: 50)
|
||||
self.titleLabel.numberOfLines = 1
|
||||
self.titleLabel.adjustsFontSizeToFitWidth = true
|
||||
self.titleLabel.minimumScaleFactor = 0.5
|
||||
self.titleLabel.textColor = self.data.textColor
|
||||
self.titleLabel.setLettersSpacing(-2)
|
||||
self.view.addSubview(self.titleLabel)
|
||||
self.views.append(self.titleLabel)
|
||||
|
||||
self.subtitleLabel.text = self.data.subtitle
|
||||
self.subtitleLabel.font = UIFont.system(type: UIFont.BoldType.Heavy, size: 50)
|
||||
self.subtitleLabel.textColor = self.data.color
|
||||
self.subtitleLabel.numberOfLines = 1
|
||||
self.subtitleLabel.adjustsFontSizeToFitWidth = true
|
||||
self.subtitleLabel.minimumScaleFactor = 0.5
|
||||
self.subtitleLabel.setLettersSpacing(-2)
|
||||
self.view.addSubview(self.subtitleLabel)
|
||||
self.views.append(self.subtitleLabel)
|
||||
|
||||
self.descriptionLabel.text = self.data.description
|
||||
if SPWelcomeViewController.hasBeenUpdated {
|
||||
self.descriptionLabel.text = self.data.changes
|
||||
}
|
||||
|
||||
self.descriptionLabel.font = UIFont.system(type: UIFont.BoldType.Regular, size: 17)
|
||||
self.descriptionLabel.textColor = self.data.textColor
|
||||
self.descriptionLabel.numberOfLines = 0
|
||||
self.descriptionLabel.setLettersSpacing(-0.2)
|
||||
self.view.addSubview(self.descriptionLabel)
|
||||
self.views.append(self.descriptionLabel)
|
||||
|
||||
self.commentLabel.text = self.data.comment
|
||||
self.commentLabel.font = UIFont.system(type: UIFont.BoldType.Regular, size: 13)
|
||||
self.commentLabel.textColor = UIColor.gray
|
||||
self.commentLabel.numberOfLines = 0
|
||||
self.commentLabel.setCenteringAlignment()
|
||||
self.view.addSubview(self.commentLabel)
|
||||
self.views.append(self.commentLabel)
|
||||
|
||||
self.button.setTitle(self.data.buttonTitle, for: UIControl.State.normal)
|
||||
self.button.backgroundColor = self.data.color
|
||||
self.button.addTarget(self, action: #selector(self.tapButton(sender:)), for: UIControl.Event.touchUpInside)
|
||||
self.view.addSubview(self.button)
|
||||
self.views.append(self.button)
|
||||
|
||||
for view in self.views {
|
||||
view.alpha = 0
|
||||
}
|
||||
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
if !self.isPresented {
|
||||
SPAnimationAlpha.showList(0.5, views: self.views, delayPerItem: 0.09)
|
||||
self.isPresented = true
|
||||
SPWelcomeViewController.updateCurrentVersion()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func tapButton(sender: UIButton) {
|
||||
SPVibration.impact(SPVibration.Style.light)
|
||||
self.data.complection(self.button)
|
||||
}
|
||||
|
||||
override func updateLayout(with size: CGSize) {
|
||||
super.updateLayout(with: size)
|
||||
|
||||
let sideSpace: CGFloat = size.width * 0.112
|
||||
|
||||
self.imageView.frame = CGRect.init(x: sideSpace, y: self.safeArea.top + (size.height * 0.1), width: 84, height: 84)
|
||||
|
||||
let space: CGFloat = size.height * 0.025
|
||||
|
||||
self.titleLabel.sizeToFit()
|
||||
self.titleLabel.frame = CGRect.init(x: sideSpace, y: self.imageView.frame.bottomYPosition + space, width: self.view.frame.width - sideSpace * 2, height: self.titleLabel.frame.height)
|
||||
|
||||
self.subtitleLabel.sizeToFit()
|
||||
self.subtitleLabel.frame = CGRect.init(x: sideSpace, y: self.titleLabel.frame.bottomYPosition - 12, width: self.view.frame.width - sideSpace * 2, height: self.subtitleLabel.frame.height)
|
||||
|
||||
self.descriptionLabel.frame = CGRect.init(x: sideSpace, y: self.subtitleLabel.frame.bottomYPosition + space, width: self.view.frame.width - sideSpace * 2, height: self.descriptionLabel.frame.height)
|
||||
self.descriptionLabel.sizeToFit()
|
||||
|
||||
self.button.sizeToFit()
|
||||
self.button.frame.origin.y = size.height - self.safeArea.bottom - space - self.button.frame.height
|
||||
self.button.setWidth(size.width - sideSpace * 2)
|
||||
self.button.center.x = size.width / 2
|
||||
|
||||
let sideSpaceForCommentLabel: CGFloat = sideSpace * 1.5
|
||||
self.commentLabel.frame = CGRect.init(x: sideSpaceForCommentLabel, y: 0, width: self.view.frame.width - sideSpaceForCommentLabel * 2, height: 0)
|
||||
self.commentLabel.sizeToFit()
|
||||
self.commentLabel.center.x = size.width / 2
|
||||
self.commentLabel.frame.origin.y = self.button.frame.origin.y - space - self.commentLabel.frame.height
|
||||
}
|
||||
}
|
||||
|
||||
extension SPWelcomeViewController {
|
||||
|
||||
static private let versionKey = "SPWelcomeControllerCurrentVersionKey"
|
||||
static private let firstLaucnhKey = "SPWelcomeControllerFirstLaucnhKey"
|
||||
|
||||
static var isFirstLaunch: Bool {
|
||||
get {
|
||||
let value = UserDefaults.standard.value(forKey: self.firstLaucnhKey) as? Bool ?? true
|
||||
if value {
|
||||
SPWelcomeViewController.updateCurrentVersion()
|
||||
}
|
||||
self.isFirstLaunch = false
|
||||
return value
|
||||
}
|
||||
set {
|
||||
UserDefaults.standard.set(newValue, forKey: self.firstLaucnhKey)
|
||||
}
|
||||
}
|
||||
|
||||
static var hasBeenUpdated: Bool {
|
||||
let userDefaults = UserDefaults.standard
|
||||
var bundleInfo = Bundle.main.infoDictionary!
|
||||
|
||||
if let currentVersion = bundleInfo["CFBundleShortVersionString"] as? String {
|
||||
if let version = userDefaults.value(forKey: self.versionKey) as? String {
|
||||
return !(version == currentVersion)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private static func updateCurrentVersion() {
|
||||
var bundleInfo = Bundle.main.infoDictionary!
|
||||
if let currentVersion = bundleInfo["CFBundleShortVersionString"] as? String {
|
||||
UserDefaults.standard.set(currentVersion, forKey: self.versionKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SPWelcomeData {
|
||||
var title: String = "Welcome to"
|
||||
var subtitle: String = "Sparrow"
|
||||
var description: String = "Simple and native welcome controller for all developers"
|
||||
var changes: String = "Description about this update"
|
||||
var comment: String = "Here you can place comment"
|
||||
var buttonTitle: String = "Continue"
|
||||
var image: UIImage = UIImage()
|
||||
var color: UIColor = SPNativeStyleKit.Colors.blue
|
||||
var backgroundColor: UIColor = UIColor.white
|
||||
var textColor: UIColor = UIColor.black
|
||||
var statusBarStyle: SPStatusBar = .dark
|
||||
var complection: (_ button: SPNativeLargeButton) -> () = { _ in }
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPAudioIconView: UIView {
|
||||
|
||||
var type: IconType {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
var color = SPNativeStyleKit.Colors.white {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
self.type = .play
|
||||
super.init(frame: CGRect.zero)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
init(type: IconType) {
|
||||
self.type = type
|
||||
super.init(frame: CGRect.zero)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
self.backgroundColor = UIColor.clear
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
super.draw(rect)
|
||||
switch type {
|
||||
case .play:
|
||||
SPCodeDraw.AudioIconPack.drawPlay(frame: rect, resizing: .aspectFit, fillColor: self.color)
|
||||
break
|
||||
case .pause:
|
||||
SPCodeDraw.AudioIconPack.drawPause(frame: rect, resizing: .aspectFit, fillColor: self.color)
|
||||
break
|
||||
case .stop:
|
||||
SPCodeDraw.AudioIconPack.drawStop(frame: rect, resizing: .aspectFit, fillColor: self.color)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
enum IconType {
|
||||
case play
|
||||
case pause
|
||||
case stop
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPGolubevIconView: UIView {
|
||||
|
||||
var type: IconType {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
var whiteColor = SPNativeStyleKit.Colors.white {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
var lightColor = UIColor.init(hex: "C4D8FB") {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
var mediumColor = UIColor.init(hex: "7AA9F8") {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
var darkColor = UIColor.init(hex: "007AFF") {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
self.type = .ball
|
||||
super.init(frame: CGRect.zero)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
init(type: IconType) {
|
||||
self.type = type
|
||||
super.init(frame: CGRect.zero)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
self.backgroundColor = UIColor.clear
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
super.draw(rect)
|
||||
switch type {
|
||||
case .camera:
|
||||
SPCodeDraw.GolubevIconPack.drawCamera(frame: rect, resizing: .aspectFit, white: self.whiteColor, light: self.lightColor, medium: self.mediumColor, dark: self.darkColor)
|
||||
break
|
||||
case .photoLibrary:
|
||||
SPCodeDraw.GolubevIconPack.drawPhotoLibrary(frame: rect, resizing: .aspectFit, white: self.whiteColor, light: self.lightColor, medium: self.mediumColor, dark: self.darkColor)
|
||||
break
|
||||
case .ball:
|
||||
SPCodeDraw.GolubevIconPack.drawBall(frame: rect, resizing: .aspectFit, white: self.whiteColor, light: self.lightColor, medium: self.mediumColor, dark: self.darkColor)
|
||||
break
|
||||
case .micro:
|
||||
SPCodeDraw.GolubevIconPack.drawMicro(frame: rect, resizing: .aspectFit, white: self.whiteColor, light: self.lightColor, medium: self.mediumColor, dark: self.darkColor)
|
||||
break
|
||||
case .book:
|
||||
SPCodeDraw.GolubevIconPack.drawBook(frame: rect, resizing: .aspectFit, white: self.whiteColor, light: self.lightColor, medium: self.mediumColor, dark: self.darkColor)
|
||||
break
|
||||
case .documents:
|
||||
SPCodeDraw.GolubevIconPack.drawDocuments(frame: rect, resizing: .aspectFit, white: self.whiteColor, light: self.lightColor, medium: self.mediumColor, dark: self.darkColor)
|
||||
break
|
||||
case .calendar:
|
||||
SPCodeDraw.GolubevIconPack.drawCalendar(frame: rect, resizing: .aspectFit, white: self.whiteColor, light: self.lightColor, medium: self.mediumColor, dark: self.darkColor)
|
||||
break
|
||||
case .compass:
|
||||
SPCodeDraw.GolubevIconPack.drawCompass(frame: rect, resizing: .aspectFit, white: self.whiteColor, light: self.lightColor, medium: self.mediumColor, dark: self.darkColor)
|
||||
break
|
||||
case .headphones:
|
||||
SPCodeDraw.GolubevIconPack.drawHeadphones(frame: rect, resizing: .aspectFit, white: self.whiteColor, light: self.lightColor, medium: self.mediumColor, dark: self.darkColor)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public enum IconType {
|
||||
case camera
|
||||
case photoLibrary
|
||||
case ball
|
||||
case micro
|
||||
case calendar
|
||||
case book
|
||||
case documents
|
||||
case compass
|
||||
case headphones
|
||||
}
|
||||
}
|
||||
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPSocialIconView: UIView {
|
||||
|
||||
var type: SPSocialNetwork {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
var color = SPNativeStyleKit.Colors.blue {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
self.type = .facebook
|
||||
super.init(frame: CGRect.zero)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
init(type: SPSocialNetwork) {
|
||||
self.type = type
|
||||
super.init(frame: CGRect.zero)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
self.backgroundColor = UIColor.clear
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
super.draw(rect)
|
||||
switch type {
|
||||
case .facebook:
|
||||
SPCodeDraw.SocialIconPack.drawFacebook(frame: rect, resizing: .aspectFit, fillColor: self.color)
|
||||
break
|
||||
case .telegram:
|
||||
SPCodeDraw.SocialIconPack.drawTelegram(frame: rect, resizing: .aspectFit, fillColor: self.color)
|
||||
break
|
||||
case .viber:
|
||||
SPCodeDraw.SocialIconPack.drawViber(frame: rect, resizing: .aspectFit, fillColor: self.color)
|
||||
break
|
||||
case .vk:
|
||||
SPCodeDraw.SocialIconPack.drawVK(frame: rect, resizing: .aspectFit, fillColor: self.color)
|
||||
break
|
||||
case .whatsapp:
|
||||
SPCodeDraw.SocialIconPack.drawWhatsapp(frame: rect, resizing: .aspectFit, fillColor: self.color)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPSystemIconView: UIView {
|
||||
|
||||
var type: SPSystemIconType {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
var color = SPNativeStyleKit.Colors.blue {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
self.type = .share
|
||||
super.init(frame: CGRect.zero)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
init(type: SPSystemIconType) {
|
||||
self.type = type
|
||||
super.init(frame: CGRect.zero)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
self.backgroundColor = UIColor.clear
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
super.draw(rect)
|
||||
switch type {
|
||||
case .share:
|
||||
SPCodeDraw.SystemIconPack.drawShare(frame: rect, resizing: .aspectFit, color: self.color)
|
||||
break
|
||||
case .close:
|
||||
SPCodeDraw.SystemIconPack.drawClose(frame: rect, resizing: .aspectFit, color: self.color)
|
||||
break
|
||||
case .favorite:
|
||||
SPCodeDraw.SystemIconPack.drawFavorite(frame: rect, resizing: .aspectFit, color: self.color)
|
||||
break
|
||||
case .favorite_fill:
|
||||
SPCodeDraw.SystemIconPack.drawFavoriteFill(frame: rect, resizing: .aspectFit, color: self.color)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user