Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c4da6ff27e | |||
| 686bbd749b | |||
| 7b151fd268 | |||
| 7bdf702703 | |||
| 0d1381d941 | |||
| 39c30a030d | |||
| 9e4f6e086f | |||
| 5bc31cdc57 | |||
| 7d53ed31e6 | |||
| 80806de694 | |||
| 8d8458cc34 | |||
| 6e09df910e | |||
| 253f60cfa9 | |||
| 81ed7b01cd | |||
| d0f120d49c | |||
| bc3555e715 | |||
| 432a1206cf | |||
| 488b19fc46 | |||
| edc2b6ea41 | |||
| 79d64599d0 | |||
| 854ab2aef2 | |||
| 04c4929e7d | |||
| 6e4e9713c3 | |||
| e37c43c8f3 | |||
| 001536c835 | |||
| 5a7d01e1e4 | |||
| a1d17d994c | |||
| f56a054b91 | |||
| bd26c4d721 | |||
| 6927d90572 | |||
| ea32992f63 | |||
| 05ba026c1f | |||
| b3f49a2a94 | |||
| c3aa4dc17e | |||
| d9f67b57f8 | |||
| 962d1a937d | |||
| bc6c7ff45e | |||
| ec7abd4a2a | |||
| 6904e0916a | |||
| 90ee3d35da | |||
| 7f998787a4 | |||
| 618da7d9ea | |||
| f062d07f94 | |||
| 926e1f643c | |||
| cdd8f4a2f4 | |||
| 30cf10c595 | |||
| 884791b05c | |||
| 4d0530e0a1 | |||
| 48741988b2 | |||
| 6297d5a5db | |||
| 3f60e48547 | |||
| 2ffe86afb9 | |||
| cf5bf12513 | |||
| 96aa1a6064 | |||
| 3241abd195 | |||
| b72f10c7bd | |||
| 40bcff3cb6 | |||
| 01cc226253 | |||
| 691045d012 | |||
| 59f52f258a | |||
| bfece3194a | |||
| 6cccf24ffa | |||
| 635878e456 | |||
| d839e0f414 | |||
| e3cbc318be | |||
| e4232ec045 | |||
| 02346c0814 | |||
| 0b98593954 | |||
| 518700f9cd | |||
| 5e02985ef2 | |||
| 539589e029 | |||
| 3434c487cd | |||
| c18e72add2 | |||
| c5e407fd80 | |||
| 3937c9caa5 | |||
| 38332bed39 | |||
| 40c7eee5de | |||
| 2e881179ee | |||
| 749640d359 | |||
| fd68fcb9f5 | |||
| 473f8ae0f1 | |||
| 75825b5e0a | |||
| f1e053d0d3 | |||
| 6208c93d40 | |||
| 8c64cd09da |
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
@@ -7,7 +7,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
self.launch(rootViewController: Controller())
|
||||
|
||||
let navigationController = UINavigationController(rootViewController: Controller())
|
||||
self.launch(rootViewController: navigationController)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class Controller: UIViewController {
|
||||
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.presentTableControllerButton.frame.origin.y = self.presentControllerButton.frame.bottomY + 10
|
||||
self.view.addSubview(self.presentTableControllerButton)
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class Controller: UIViewController {
|
||||
let transitionDelegate = SPStorkTransitioningDelegate()
|
||||
modal.transitioningDelegate = transitionDelegate
|
||||
modal.modalPresentationStyle = .custom
|
||||
present(modal, animated: true, completion: nil)
|
||||
self.present(modal, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc func presentModalTableViewController() {
|
||||
@@ -38,6 +38,6 @@ class Controller: UIViewController {
|
||||
let transitionDelegate = SPStorkTransitioningDelegate()
|
||||
modal.transitioningDelegate = transitionDelegate
|
||||
modal.modalPresentationStyle = .custom
|
||||
present(modal, animated: true, completion: nil)
|
||||
self.present(modal, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
Regular → Executable
+15
-9
@@ -28,13 +28,28 @@ public struct SPStorkController {
|
||||
if let presentationController = controller.presentationController as? SPStorkPresentationController {
|
||||
let translation = -(scrollView.contentOffset.y + scrollView.contentInset.top)
|
||||
if translation >= 0 {
|
||||
|
||||
if controller.isBeingPresented { return }
|
||||
|
||||
scrollView.subviews.forEach {
|
||||
$0.transform = CGAffineTransform(translationX: 0, y: -translation)
|
||||
}
|
||||
|
||||
presentationController.setIndicator(style: scrollView.isDragging ? .line : .arrow)
|
||||
|
||||
if translation >= presentationController.translateForDismiss * 0.65 {
|
||||
if !scrollView.isDragging {
|
||||
presentationController.presentedViewController.dismiss(animated: true, completion: nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if presentationController.pan?.state != UIGestureRecognizer.State.changed {
|
||||
presentationController.scrollViewDidScroll(translation)
|
||||
}
|
||||
|
||||
} else {
|
||||
presentationController.setIndicator(style: .arrow)
|
||||
presentationController.scrollViewDidScroll(0)
|
||||
}
|
||||
}
|
||||
@@ -63,12 +78,3 @@ public struct SPStorkController {
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
var isPresentedAsStork: Bool {
|
||||
return transitioningDelegate is SPStorkTransitioningDelegate
|
||||
&& modalPresentationStyle == .custom
|
||||
&& presentingViewController != nil
|
||||
}
|
||||
}
|
||||
|
||||
Regular → Executable
Regular → Executable
+8
-2
@@ -42,6 +42,13 @@ class SPStorkIndicatorView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
var color: UIColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1) {
|
||||
didSet {
|
||||
self.leftView.backgroundColor = self.color
|
||||
self.rightView.backgroundColor = self.color
|
||||
}
|
||||
}
|
||||
|
||||
private var leftView: UIView = UIView()
|
||||
private var rightView: UIView = UIView()
|
||||
|
||||
@@ -50,8 +57,7 @@ class SPStorkIndicatorView: UIView {
|
||||
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)
|
||||
self.color = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
|
||||
Regular → Executable
+36
-13
@@ -23,10 +23,13 @@ import UIKit
|
||||
|
||||
class SPStorkPresentationController: UIPresentationController, UIGestureRecognizerDelegate {
|
||||
|
||||
var isSwipeToDismissEnabled: Bool = true
|
||||
var isTapAroundToDismissEnabled: Bool = true
|
||||
var swipeToDismissEnabled: Bool = true
|
||||
var tapAroundToDismissEnabled: Bool = true
|
||||
var showIndicator: Bool = true
|
||||
var indicatorColor: UIColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
|
||||
var customHeight: CGFloat? = nil
|
||||
var translateForDismiss: CGFloat = 240
|
||||
|
||||
var transitioningDelegate: SPStorkTransitioningDelegate?
|
||||
|
||||
var pan: UIPanGestureRecognizer?
|
||||
@@ -65,7 +68,13 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
|
||||
override var frameOfPresentedViewInContainerView: CGRect {
|
||||
guard let containerView = containerView else { return .zero }
|
||||
let additionTranslate = containerView.bounds.height - (self.customHeight ?? containerView.bounds.height)
|
||||
|
||||
var customHeight = self.customHeight ?? containerView.bounds.height
|
||||
if customHeight > containerView.bounds.height {
|
||||
customHeight = containerView.bounds.height
|
||||
print("SPStorkController - Custom height change to default value. Your height more maximum value")
|
||||
}
|
||||
let additionTranslate = containerView.bounds.height - customHeight
|
||||
let yOffset: CGFloat = self.topSpace + 13 + additionTranslate
|
||||
return CGRect(x: 0, y: yOffset, width: containerView.bounds.width, height: containerView.bounds.height - yOffset)
|
||||
}
|
||||
@@ -76,6 +85,10 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
guard let containerView = self.containerView, let presentedView = self.presentedView, let window = containerView.window else { return }
|
||||
|
||||
if self.showIndicator {
|
||||
self.indicatorView.color = self.indicatorColor
|
||||
let tap = UITapGestureRecognizer.init(target: self, action: #selector(self.dismissAction))
|
||||
tap.cancelsTouchesInView = false
|
||||
self.indicatorView.addGestureRecognizer(tap)
|
||||
presentedView.addSubview(self.indicatorView)
|
||||
}
|
||||
self.updateLayoutIndicator()
|
||||
@@ -122,8 +135,9 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
containerView.insertSubview(snapshotView, aboveSubview: self.backgroundView)
|
||||
snapshotView.frame = initialFrame
|
||||
snapshotView.transform = transformForSnapshotView
|
||||
snapshotView.alpha = self.alpha
|
||||
snapshotView.alpha = 1 - self.alpha
|
||||
snapshotView.layer.cornerRadius = self.cornerRadius
|
||||
snapshotView.contentMode = .top
|
||||
snapshotView.layer.masksToBounds = true
|
||||
rootSnapshotView = snapshotView
|
||||
|
||||
@@ -158,13 +172,13 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
self.snapshotViewContainer.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
|
||||
self.updateSnapshotAspectRatio()
|
||||
|
||||
if self.isTapAroundToDismissEnabled {
|
||||
self.tap = UITapGestureRecognizer.init(target: self, action: #selector(self.handleTap))
|
||||
if self.tapAroundToDismissEnabled {
|
||||
self.tap = UITapGestureRecognizer.init(target: self, action: #selector(self.dismissAction))
|
||||
self.tap?.cancelsTouchesInView = false
|
||||
self.snapshotViewContainer.addGestureRecognizer(self.tap!)
|
||||
}
|
||||
|
||||
if self.isSwipeToDismissEnabled {
|
||||
if self.swipeToDismissEnabled {
|
||||
self.pan = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan))
|
||||
self.pan!.delegate = self
|
||||
self.pan!.maximumNumberOfTouches = 1
|
||||
@@ -173,7 +187,9 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
}
|
||||
}
|
||||
|
||||
@objc func handleTap() {
|
||||
@objc func dismissAction() {
|
||||
self.presentingViewController.view.endEditing(true)
|
||||
self.presentedViewController.view.endEditing(true)
|
||||
self.presentedViewController.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@@ -212,6 +228,7 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
containerView.insertSubview(snapshotView, aboveSubview: backgroundView)
|
||||
snapshotView.frame = initialFrame
|
||||
snapshotView.transform = initialTransform
|
||||
snapshotView.contentMode = .top
|
||||
rootSnapshotView = snapshotView
|
||||
snapshotView.layer.cornerRadius = self.cornerRadius
|
||||
snapshotView.layer.masksToBounds = true
|
||||
@@ -219,7 +236,7 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
let snapshotRoundedView = UIView()
|
||||
snapshotRoundedView.layer.cornerRadius = self.cornerRadius
|
||||
snapshotRoundedView.layer.masksToBounds = true
|
||||
snapshotRoundedView.backgroundColor = UIColor.black.withAlphaComponent(1 - self.alpha)
|
||||
snapshotRoundedView.backgroundColor = UIColor.black.withAlphaComponent(self.alpha)
|
||||
containerView.insertSubview(snapshotRoundedView, aboveSubview: snapshotView)
|
||||
snapshotRoundedView.frame = initialFrame
|
||||
snapshotRoundedView.transform = initialTransform
|
||||
@@ -255,17 +272,19 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
extension SPStorkPresentationController {
|
||||
|
||||
@objc func handlePan(gestureRecognizer: UIPanGestureRecognizer) {
|
||||
guard gestureRecognizer.isEqual(pan), self.isSwipeToDismissEnabled else { return }
|
||||
guard gestureRecognizer.isEqual(self.pan), self.swipeToDismissEnabled else { return }
|
||||
|
||||
switch gestureRecognizer.state {
|
||||
case .began:
|
||||
self.workGester = true
|
||||
self.indicatorView.style = .line
|
||||
self.presentingViewController.view.layer.removeAllAnimations()
|
||||
self.presentingViewController.view.endEditing(true)
|
||||
self.presentedViewController.view.endEditing(true)
|
||||
gestureRecognizer.setTranslation(CGPoint(x: 0, y: 0), in: containerView)
|
||||
case .changed:
|
||||
self.workGester = true
|
||||
if self.isSwipeToDismissEnabled {
|
||||
if self.swipeToDismissEnabled {
|
||||
let translation = gestureRecognizer.translation(in: presentedView)
|
||||
self.updatePresentedViewForTranslation(inVerticalDirection: translation.y)
|
||||
} else {
|
||||
@@ -274,8 +293,8 @@ extension SPStorkPresentationController {
|
||||
case .ended:
|
||||
self.workGester = false
|
||||
let translation = gestureRecognizer.translation(in: presentedView).y
|
||||
if translation >= 240 {
|
||||
presentedViewController.dismiss(animated: true, completion: nil)
|
||||
if translation >= self.translateForDismiss {
|
||||
self.presentedViewController.dismiss(animated: true, completion: nil)
|
||||
} else {
|
||||
self.indicatorView.style = .arrow
|
||||
UIView.animate(
|
||||
@@ -306,6 +325,10 @@ extension SPStorkPresentationController {
|
||||
self.updateSnapshot()
|
||||
}
|
||||
|
||||
func setIndicator(style: SPStorkIndicatorView.Style) {
|
||||
self.indicatorView.style = style
|
||||
}
|
||||
|
||||
private func updatePresentedViewForTranslation(inVerticalDirection translation: CGFloat) {
|
||||
if self.startDismissing { return }
|
||||
|
||||
|
||||
Regular → Executable
+4
-5
@@ -25,15 +25,14 @@ final class SPStorkPresentingAnimationController: NSObject, UIViewControllerAnim
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
|
||||
guard let presentedViewController = transitionContext.viewController(forKey: .to) else {
|
||||
return
|
||||
}
|
||||
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)
|
||||
presentedViewController.view.frame = finalFrameForPresentedView
|
||||
presentedViewController.view.frame.origin.y = containerView.bounds.height
|
||||
|
||||
UIView.animate(
|
||||
withDuration: transitionDuration(using: transitionContext),
|
||||
|
||||
Regular → Executable
+8
-4
@@ -23,17 +23,21 @@ import UIKit
|
||||
|
||||
public final class SPStorkTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
|
||||
|
||||
public var isSwipeToDismissEnabled: Bool = true
|
||||
public var isTapAroundToDismissEnabled: Bool = true
|
||||
public var swipeToDismissEnabled: Bool = true
|
||||
public var tapAroundToDismissEnabled: Bool = true
|
||||
public var showIndicator: Bool = true
|
||||
public var indicatorColor: UIColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
|
||||
public var customHeight: CGFloat? = nil
|
||||
public var translateForDismiss: CGFloat = 240
|
||||
|
||||
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.swipeToDismissEnabled = self.swipeToDismissEnabled
|
||||
controller.tapAroundToDismissEnabled = self.tapAroundToDismissEnabled
|
||||
controller.showIndicator = self.showIndicator
|
||||
controller.indicatorColor = self.indicatorColor
|
||||
controller.customHeight = self.customHeight
|
||||
controller.translateForDismiss = self.translateForDismiss
|
||||
controller.transitioningDelegate = self
|
||||
return controller
|
||||
}
|
||||
|
||||
Executable
+40
@@ -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
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
public var isPresentedAsStork: Bool {
|
||||
return transitioningDelegate is SPStorkTransitioningDelegate
|
||||
&& modalPresentationStyle == .custom
|
||||
&& presentingViewController != nil
|
||||
}
|
||||
|
||||
public func presentAsStork(_ controller: UIViewController, height: CGFloat? = nil, complection: (() -> Void)? = nil) {
|
||||
let transitionDelegate = SPStorkTransitioningDelegate()
|
||||
transitionDelegate.customHeight = height
|
||||
controller.transitioningDelegate = transitionDelegate
|
||||
controller.modalPresentationStyle = .custom
|
||||
controller.modalPresentationCapturesStatusBarAppearance = true
|
||||
self.present(controller, animated: true, completion: complection)
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
public class SPAnimation {
|
||||
|
||||
static func animate(_ duration: TimeInterval,
|
||||
public static func animate(_ duration: TimeInterval,
|
||||
animations: (() -> Void)!,
|
||||
delay: TimeInterval = 0,
|
||||
options: UIView.AnimationOptions = [],
|
||||
@@ -40,14 +40,14 @@ public class SPAnimation {
|
||||
})
|
||||
}
|
||||
|
||||
static func animateWithRepeatition(_ duration: TimeInterval,
|
||||
public static func animateWithRepeatition(_ duration: TimeInterval,
|
||||
animations: (() -> Void)!,
|
||||
delay: TimeInterval = 0,
|
||||
options: UIView.AnimationOptions = [],
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
|
||||
var optionsWithRepeatition = options
|
||||
optionsWithRepeatition.insert([.autoreverse, .repeat])
|
||||
optionsWithRepeatition.insert([.autoreverse, .repeat, .allowUserInteraction])
|
||||
|
||||
self.animate(
|
||||
duration,
|
||||
|
||||
@@ -23,11 +23,11 @@ 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
|
||||
public static let durationListAnimation: TimeInterval = 0.45
|
||||
public static let coefLenthForTransition: CGFloat = 2.8
|
||||
public static let delayPerItem: TimeInterval = 0.09
|
||||
|
||||
static func hideList(_ duration: TimeInterval = durationListAnimation,
|
||||
public static func hideList(_ duration: TimeInterval = durationListAnimation,
|
||||
views: [UIView],
|
||||
delayPerItem: TimeInterval = delayPerItem,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
@@ -51,7 +51,7 @@ public class SPAnimationAlpha {
|
||||
}
|
||||
}
|
||||
|
||||
static func hideReverseList(_ duration: TimeInterval = durationListAnimation,
|
||||
public static func hideReverseList(_ duration: TimeInterval = durationListAnimation,
|
||||
views: [UIView],
|
||||
delayPerItem: TimeInterval = delayPerItem,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
@@ -76,7 +76,7 @@ public class SPAnimationAlpha {
|
||||
}
|
||||
}
|
||||
|
||||
static func showList(_ duration: TimeInterval = durationListAnimation,
|
||||
public static func showList(_ duration: TimeInterval = durationListAnimation,
|
||||
views: [UIView],
|
||||
delayPerItem: TimeInterval = delayPerItem,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
|
||||
@@ -23,10 +23,10 @@ import UIKit
|
||||
|
||||
public class SPAnimationSpring {
|
||||
|
||||
fileprivate static let spring: CGFloat = 1
|
||||
fileprivate static let velocity: CGFloat = 1
|
||||
public static let spring: CGFloat = 1
|
||||
public static let velocity: CGFloat = 1
|
||||
|
||||
static func animate(_ duration: TimeInterval,
|
||||
public static func animate(_ duration: TimeInterval,
|
||||
animations: (() -> Void)!,
|
||||
delay: TimeInterval = 0,
|
||||
spring: CGFloat = spring,
|
||||
@@ -47,7 +47,7 @@ public class SPAnimationSpring {
|
||||
})
|
||||
}
|
||||
|
||||
static func animateWithRepeatition(_ duration: TimeInterval,
|
||||
public static func animateWithRepeatition(_ duration: TimeInterval,
|
||||
animations: (() -> Void)!,
|
||||
delay: TimeInterval = 0,
|
||||
spring: CGFloat = spring,
|
||||
|
||||
@@ -23,11 +23,11 @@ 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
|
||||
public static let durationListAnimation: TimeInterval = 0.45
|
||||
public static let coefLenthForTransition: CGFloat = 2.8
|
||||
public static let delayPerItem: TimeInterval = 0.09
|
||||
|
||||
static func hide(_ duration: TimeInterval,
|
||||
public static func hide(_ duration: TimeInterval,
|
||||
view: UIView,
|
||||
delay: TimeInterval = 0,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
@@ -48,7 +48,7 @@ public class SPAnimationUpward {
|
||||
})
|
||||
}
|
||||
|
||||
static func hideList(_ duration: TimeInterval = durationListAnimation,
|
||||
public static func hideList(_ duration: TimeInterval = durationListAnimation,
|
||||
views: [UIView],
|
||||
delayPerItem: TimeInterval = delayPerItem,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
@@ -69,7 +69,7 @@ public class SPAnimationUpward {
|
||||
|
||||
}
|
||||
|
||||
static func show(_ duration: TimeInterval,
|
||||
public static func show(_ duration: TimeInterval,
|
||||
view: UIView,
|
||||
delay: TimeInterval = 0,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
@@ -93,7 +93,7 @@ public class SPAnimationUpward {
|
||||
})
|
||||
}
|
||||
|
||||
static func showList(_ duration: TimeInterval = durationListAnimation,
|
||||
public static func showList(_ duration: TimeInterval = durationListAnimation,
|
||||
views: [UIView],
|
||||
delayPerItem: TimeInterval = delayPerItem,
|
||||
options: UIView.AnimationOptions = [],
|
||||
|
||||
@@ -20,22 +20,31 @@
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
import StoreKit
|
||||
|
||||
struct SPApp {
|
||||
|
||||
static var udid: String? {
|
||||
public static var udid: String? {
|
||||
return UIDevice.current.identifierForVendor?.uuidString
|
||||
}
|
||||
|
||||
static var displayName: String? {
|
||||
public static var displayName: String? {
|
||||
return Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
|
||||
}
|
||||
|
||||
static var rootController: UIViewController? {
|
||||
public static var rootController: UIViewController? {
|
||||
return UIApplication.shared.keyWindow?.rootViewController
|
||||
}
|
||||
|
||||
static func set(rootController: UIViewController, animatable: Bool = true) {
|
||||
public static var safeArea: UIEdgeInsets {
|
||||
if #available(iOS 11.0, *) {
|
||||
return UIApplication.shared.keyWindow?.safeArea ?? UIEdgeInsets.zero
|
||||
} else {
|
||||
return UIEdgeInsets.zero
|
||||
}
|
||||
}
|
||||
|
||||
public static func set(rootController: UIViewController, animatable: Bool = true) {
|
||||
|
||||
rootController.view.frame = UIScreen.main.bounds
|
||||
|
||||
@@ -56,5 +65,12 @@ struct SPApp {
|
||||
}
|
||||
}
|
||||
|
||||
public static func set(elementsColor: UIColor) {
|
||||
UINavigationController.elementsColor = elementsColor
|
||||
UIAlertController.elementsColor = elementsColor
|
||||
UITabBarController.elementsColor = elementsColor
|
||||
UITabBar.appearance().tintColor = elementsColor
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
+16
-17
@@ -21,25 +21,24 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
struct SPLaunch {
|
||||
extension SPApp {
|
||||
|
||||
static func run() {
|
||||
self.count += 1
|
||||
}
|
||||
|
||||
static var count: Int {
|
||||
get {
|
||||
return UserDefaults.standard.value(forKey: "SPLaunchCount") as? Int ?? 0
|
||||
public struct Badge {
|
||||
|
||||
public static var number: Int {
|
||||
get {
|
||||
return UIApplication.shared.applicationIconBadgeNumber
|
||||
}
|
||||
set {
|
||||
UIApplication.shared.applicationIconBadgeNumber = newValue
|
||||
}
|
||||
}
|
||||
set {
|
||||
UserDefaults.standard.set(newValue, forKey: "SPLaunchCount")
|
||||
|
||||
public static func reset() {
|
||||
UIApplication.shared.applicationIconBadgeNumber = 0
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
static var isFirstOpen: Bool {
|
||||
return (self.count == 1) || (self.count == 0)
|
||||
}
|
||||
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
+21
-16
@@ -21,22 +21,27 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPImageCollectionViewCell: SPCollectionContainerCell<SPDownloadingImageView> {
|
||||
extension SPApp {
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.view.layer.cornerRadius = 10
|
||||
self.view.contentMode = .scaleAspectFill
|
||||
self.view.setNative()
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
self.view.contentMode = .scaleAspectFill
|
||||
self.view.startLoading()
|
||||
public struct Launch {
|
||||
|
||||
public static func run() {
|
||||
self.count += 1
|
||||
}
|
||||
|
||||
public static var count: Int {
|
||||
get {
|
||||
return UserDefaults.standard.value(forKey: "SPLaunchCount") as? Int ?? 0
|
||||
}
|
||||
set {
|
||||
UserDefaults.standard.set(newValue, forKey: "SPLaunchCount")
|
||||
}
|
||||
}
|
||||
|
||||
public static var isFirstLaunch: Bool {
|
||||
return (self.count == 1) || (self.count == 0)
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
}
|
||||
@@ -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 SafariServices
|
||||
|
||||
extension SPApp {
|
||||
|
||||
public static func open(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("SPApp - Photos opened: \(success)")
|
||||
})
|
||||
} else {
|
||||
UIApplication.shared.openURL(settingsUrl as URL)
|
||||
}
|
||||
} else {
|
||||
print("SPApp - 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("SPApp - Settings opened: \(success)")
|
||||
})
|
||||
} else {
|
||||
UIApplication.shared.openURL(settingsUrl as URL)
|
||||
}
|
||||
} else {
|
||||
print("SPApp - Settings not opened")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func open(link: String, redirect: Bool) {
|
||||
|
||||
guard let url = URL(string: link) else {
|
||||
print("SPOpener - can not create URL")
|
||||
return
|
||||
}
|
||||
|
||||
if redirect {
|
||||
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
|
||||
} else {
|
||||
if let rootController = SPApp.rootController {
|
||||
let safariController = SFSafariViewController.init(url: url)
|
||||
rootController.present(safariController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
|
||||
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
|
||||
}
|
||||
@@ -24,12 +24,12 @@ import StoreKit
|
||||
|
||||
struct SPAppStore {
|
||||
|
||||
static func link(appID: String) -> String {
|
||||
public 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)"),
|
||||
public static func open(app id: String) {
|
||||
if let url = URL(string: "itms-apps://itunes.apple.com/app/id\(id)"),
|
||||
UIApplication.shared.canOpenURL(url) {
|
||||
if #available(iOS 10.0, *) {
|
||||
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
|
||||
@@ -39,7 +39,7 @@ struct SPAppStore {
|
||||
}
|
||||
}
|
||||
|
||||
static func requestReview(appID: String, force: Bool) {
|
||||
public 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) {
|
||||
@@ -56,7 +56,13 @@ struct SPAppStore {
|
||||
}
|
||||
}
|
||||
|
||||
static func isUpdateAvailable(completion: @escaping (Bool)->()) {
|
||||
public static func requestReview() {
|
||||
if #available(iOS 10.3, *) {
|
||||
SKStoreReviewController.requestReview()
|
||||
}
|
||||
}
|
||||
|
||||
public static func isUpdateAvailable(completion: @escaping (Bool)->()) {
|
||||
|
||||
guard let info = Bundle.main.infoDictionary,
|
||||
let currentVersion = info["CFBundleShortVersionString"] as? String,
|
||||
@@ -128,7 +134,6 @@ extension String {
|
||||
}
|
||||
}
|
||||
|
||||
// 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)})
|
||||
}
|
||||
|
||||
@@ -24,19 +24,18 @@ import AVFoundation
|
||||
|
||||
public struct SPAudio {
|
||||
|
||||
static func notStopBackgroundMusic() {
|
||||
public 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 {
|
||||
|
||||
print("SPAudio - notStopBackgroundMusic, error")
|
||||
}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
// Helper function inserted by Swift 4.2 migrator.
|
||||
fileprivate func convertFromAVAudioSessionCategory(_ input: AVAudioSession.Category) -> String {
|
||||
return input.rawValue
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public class SPAudioPlayer: NSObject, AVAudioPlayerDelegate {
|
||||
fileprivate var player: AVAudioPlayer = AVAudioPlayer()
|
||||
fileprivate var endPlayingComplection: (()->())? = nil
|
||||
|
||||
func play(fileName: String, complection: (()->())? = nil) {
|
||||
public func play(fileName: String, complection: (()->())? = nil) {
|
||||
self.endPlayingComplection?()
|
||||
self.player = AVAudioPlayer()
|
||||
let url = Bundle.main.url(forResource: fileName, withExtension: nil)
|
||||
@@ -47,7 +47,7 @@ public class SPAudioPlayer: NSObject, AVAudioPlayerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func stop() {
|
||||
public func stop() {
|
||||
player.stop()
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
struct SPBufer {
|
||||
|
||||
static var text: String? {
|
||||
public static var text: String? {
|
||||
get {
|
||||
return UIPasteboard.general.string
|
||||
}
|
||||
@@ -21,4 +21,4 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
struct SPCodeDraw { private init(){} }
|
||||
public struct SPCodeDraw { private init() {} }
|
||||
|
||||
+974
-108
File diff suppressed because it is too large
Load Diff
@@ -23,15 +23,19 @@ 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)
|
||||
])
|
||||
public static func setEqualSizeSuperview(for view: UIView) {
|
||||
if let superView = view.superview {
|
||||
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 init() {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
public func delay(_ delay:Double, closure:@escaping ()->()) {
|
||||
public func delay(_ delay:Double, closure: @escaping ()->()) {
|
||||
let when = DispatchTime.now() + delay
|
||||
DispatchQueue.main.asyncAfter(deadline: when) {
|
||||
closure()
|
||||
|
||||
@@ -21,19 +21,19 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
struct SPDevice {
|
||||
public struct SPDevice {
|
||||
|
||||
static var isIphone: Bool {
|
||||
public static var iphone: Bool {
|
||||
return UIDevice.current.userInterfaceIdiom == .phone
|
||||
}
|
||||
|
||||
static var isIpad: Bool {
|
||||
public static var ipad: Bool {
|
||||
return UIDevice.current.userInterfaceIdiom == .pad
|
||||
}
|
||||
|
||||
struct Orientation {
|
||||
public struct Orientation {
|
||||
|
||||
static var isPortrait: Bool {
|
||||
public static var isPortrait: Bool {
|
||||
var isPortraitOrientation = true
|
||||
if UIDevice.current.orientation.isValidInterfaceOrientation {
|
||||
if UIDevice.current.orientation.isPortrait {
|
||||
|
||||
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
struct SPDownloader {
|
||||
|
||||
static func image(link: String, withComplection complection: @escaping (UIImage?) -> ()) {
|
||||
public static func image(link: String, withComplection complection: @escaping (UIImage?) -> ()) {
|
||||
guard let url = URL(string: link) else {
|
||||
DispatchQueue.main.async {
|
||||
complection(nil)
|
||||
|
||||
@@ -23,7 +23,7 @@ import Foundation
|
||||
|
||||
extension Array {
|
||||
|
||||
func get(count: Int) -> Array {
|
||||
public func get(count: Int) -> Array {
|
||||
if (count < self.count) { return Array(self[0..<count]) }
|
||||
return Array(self)
|
||||
}
|
||||
@@ -31,7 +31,7 @@ extension Array {
|
||||
|
||||
extension Array where Element: Equatable {
|
||||
|
||||
mutating func removeDuplicates() {
|
||||
public mutating func removeDuplicates() {
|
||||
var result = [Element]()
|
||||
for value in self {
|
||||
if result.contains(value) == false { result.append(value) }
|
||||
@@ -42,7 +42,7 @@ extension Array where Element: Equatable {
|
||||
|
||||
extension Array where Element: Hashable {
|
||||
|
||||
func after(item: Element) -> Element? {
|
||||
public func after(item: Element) -> Element? {
|
||||
if let index = self.index(of: item), index + 1 < self.count { return self[index + 1] }
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -23,17 +23,29 @@ import UIKit
|
||||
|
||||
extension CGRect {
|
||||
|
||||
var bottomXPosition: CGFloat {
|
||||
public var bottomX: CGFloat {
|
||||
get { return self.origin.x + self.width }
|
||||
set { self.origin.x = newValue - self.width }
|
||||
}
|
||||
|
||||
var bottomYPosition: CGFloat {
|
||||
public var bottomY: CGFloat {
|
||||
get { return self.origin.y + self.height }
|
||||
set { self.origin.y = newValue - self.height }
|
||||
}
|
||||
|
||||
var minSideSize: CGFloat {
|
||||
public var minSide: CGFloat {
|
||||
return min(self.width, self.height)
|
||||
}
|
||||
|
||||
public mutating func set(width: CGFloat) {
|
||||
self = CGRect.init(x: self.origin.x, y: self.origin.y, width: width, height: self.height)
|
||||
}
|
||||
|
||||
public mutating func set(height: CGFloat) {
|
||||
self = CGRect.init(x: self.origin.x, y: self.origin.y, width: self.width, height: height)
|
||||
}
|
||||
|
||||
public mutating func set(width: CGFloat, height: CGFloat) {
|
||||
self = CGRect.init(x: self.origin.x, y: self.origin.y, width: width, height: height)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@ import UIKit
|
||||
|
||||
extension CGSize {
|
||||
|
||||
func resize(width: CGFloat) -> CGSize {
|
||||
public 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 {
|
||||
public func resize(height: CGFloat) -> CGSize {
|
||||
let relativeSideSize = self.width / self.height
|
||||
let newWidth = height * relativeSideSize
|
||||
return CGSize.init(width: newWidth, height: height)
|
||||
|
||||
+11
-13
@@ -19,22 +19,20 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
import Foundation
|
||||
|
||||
public struct SPBadge {
|
||||
extension Date {
|
||||
|
||||
static func reset() {
|
||||
UIApplication.shared.applicationIconBadgeNumber = 0
|
||||
public func format(mask: String) -> String {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = mask
|
||||
return dateFormatter.string(from: self)
|
||||
}
|
||||
|
||||
static var number: Int {
|
||||
get {
|
||||
return UIApplication.shared.applicationIconBadgeNumber
|
||||
}
|
||||
set {
|
||||
UIApplication.shared.applicationIconBadgeNumber = newValue
|
||||
}
|
||||
public static func create(from value: String) -> Date? {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "dd.MM.yyyy HH:mm"
|
||||
let date = formatter.date(from: value)
|
||||
return date
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
@@ -24,49 +24,65 @@ import UIKit
|
||||
|
||||
extension String {
|
||||
|
||||
mutating func dropLast(substring: String) {
|
||||
public var digits: String {
|
||||
return components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
|
||||
}
|
||||
|
||||
public mutating func dropLast(substring: String) {
|
||||
if self.hasSuffix(substring) {
|
||||
self = String(dropLast(substring.count))
|
||||
}
|
||||
}
|
||||
|
||||
mutating func dropFirst(substring: String) {
|
||||
public mutating func dropFirst(substring: String) {
|
||||
if self.hasPrefix(substring) {
|
||||
self = String(dropFirst(substring.count))
|
||||
}
|
||||
}
|
||||
|
||||
func uppercasedFirstLetter() -> String {
|
||||
public func uppercasedFirstLetter() -> String {
|
||||
let lowercaseSctring = self.lowercased()
|
||||
return lowercaseSctring.prefix(1).uppercased() + lowercaseSctring.dropFirst()
|
||||
}
|
||||
|
||||
mutating func uppercaseFirstLetter() {
|
||||
public mutating func uppercaseFirstLetter() {
|
||||
self = self.uppercasedFirstLetter()
|
||||
}
|
||||
|
||||
func removeAllSpaces() -> String {
|
||||
public func removeAllSpaces() -> String {
|
||||
return self.components(separatedBy: .whitespaces).joined()
|
||||
}
|
||||
|
||||
mutating func removeAllSpaces() {
|
||||
public mutating func removeAllSpaces() {
|
||||
self = self.removeAllSpaces()
|
||||
}
|
||||
|
||||
var isEmail: Bool {
|
||||
public 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 {
|
||||
public var isLink: Bool {
|
||||
if let url = URL(string: self) {
|
||||
return UIApplication.shared.canOpenURL(url)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
mutating func replace(_ replacingString: String, with newString: String) {
|
||||
public var isEmpty: Bool {
|
||||
return (self.removeAllSpaces() == "")
|
||||
}
|
||||
|
||||
public var words: [String] {
|
||||
return components(separatedBy: .punctuationCharacters).joined().components(separatedBy: .whitespaces)
|
||||
}
|
||||
|
||||
public mutating func replace(_ replacingString: String, with newString: String) {
|
||||
self = self.replacingOccurrences(of: replacingString, with: newString)
|
||||
}
|
||||
|
||||
public func replace(_ replacingString: String, with newString: String) -> String {
|
||||
return self.replacingOccurrences(of: replacingString, with: newString)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
extension UITextField {
|
||||
|
||||
@IBInspectable var placeholderColor: UIColor? {
|
||||
@IBInspectable public var placeholderColor: UIColor? {
|
||||
get {
|
||||
return self.placeholderColor
|
||||
}
|
||||
|
||||
+19
-16
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
extension UIAlertController {
|
||||
|
||||
static var elementsColor: UIColor {
|
||||
public static var elementsColor: UIColor {
|
||||
get {
|
||||
return UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor
|
||||
}
|
||||
@@ -39,13 +39,12 @@ extension UIAlertController {
|
||||
preferredStyle: .alert
|
||||
)
|
||||
|
||||
if cancelButtonTitle != nil {
|
||||
ac.addAction(UIAlertAction.init(
|
||||
title: cancelButtonTitle!,
|
||||
style: UIAlertAction.Style.cancel,
|
||||
handler: nil)
|
||||
)
|
||||
}
|
||||
guard cancelButtonTitle != nil else { return }
|
||||
ac.addAction(UIAlertAction.init(
|
||||
title: cancelButtonTitle!,
|
||||
style: UIAlertAction.Style.cancel,
|
||||
handler: nil)
|
||||
)
|
||||
|
||||
ac.addAction(UIAlertAction.init(
|
||||
title: buttonTitle,
|
||||
@@ -64,10 +63,8 @@ extension UIAlertController {
|
||||
preferredStyle: .actionSheet
|
||||
)
|
||||
|
||||
var style = UIAlertAction.Style.default
|
||||
if isDestructive {
|
||||
style = .destructive
|
||||
}
|
||||
let style = isDestructive ? .destructive : UIAlertAction.Style.default
|
||||
|
||||
ac.addAction(UIAlertAction.init(
|
||||
title: buttonTitle,
|
||||
style: style,
|
||||
@@ -76,28 +73,34 @@ extension UIAlertController {
|
||||
}))
|
||||
ac.addAction(UIAlertAction.init(
|
||||
title: cancelButtonTitle,
|
||||
style: UIAlertAction.Style.default,
|
||||
style: UIAlertAction.Style.cancel,
|
||||
handler: { (action) in
|
||||
complection(false)
|
||||
}))
|
||||
|
||||
viewController.present(ac, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIAlertController {
|
||||
|
||||
func addAction(title: String, complection: @escaping ()->()) {
|
||||
public 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 ()->()) {
|
||||
public func addDestructiveAction(title: String, complection: @escaping ()->()) {
|
||||
let action = UIAlertAction(title: title, style: .destructive) { (action) in
|
||||
complection()
|
||||
}
|
||||
self.addAction(action)
|
||||
}
|
||||
|
||||
public func addCancelAction(title: String, complection: @escaping ()->() = {}) {
|
||||
let action = UIAlertAction(title: title, style: .cancel) { (action) in
|
||||
complection()
|
||||
}
|
||||
self.addAction(action)
|
||||
}
|
||||
}
|
||||
|
||||
+21
-13
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
extension UIButton {
|
||||
|
||||
typealias UIButtonTargetClosure = () -> ()
|
||||
public typealias UIButtonTargetClosure = () -> ()
|
||||
|
||||
private class ClosureWrapper: NSObject {
|
||||
let closure: UIButtonTargetClosure
|
||||
@@ -47,7 +47,7 @@ extension UIButton {
|
||||
}
|
||||
}
|
||||
|
||||
func target(_ action: @escaping UIButtonTargetClosure) {
|
||||
public func target(_ action: @escaping UIButtonTargetClosure) {
|
||||
targetClosure = action
|
||||
addTarget(self, action: #selector(UIButton.targetAction), for: .touchUpInside)
|
||||
}
|
||||
@@ -60,20 +60,28 @@ extension UIButton {
|
||||
|
||||
extension UIButton {
|
||||
|
||||
func setTitle(_ title: String) {
|
||||
public func setTitle(_ title: String, color: UIColor? = nil) {
|
||||
self.setTitle(title, for: .normal)
|
||||
if let color = color {
|
||||
self.setTitleColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
func setTitleColor(_ color: UIColor) {
|
||||
public func setTitleColor(_ color: UIColor) {
|
||||
self.setTitleColor(color, for: .normal)
|
||||
self.setTitleColor(color.withAlphaComponent(0.7), for: .highlighted)
|
||||
}
|
||||
|
||||
func removeAllTargets() {
|
||||
public func setImage(_ image: UIImage) {
|
||||
self.setImage(image, for: .normal)
|
||||
self.imageView?.contentMode = .scaleAspectFit
|
||||
}
|
||||
|
||||
public func removeAllTargets() {
|
||||
self.removeTarget(nil, action: nil, for: .allEvents)
|
||||
}
|
||||
|
||||
func showText(_ text: String, withComplection completion: (() -> Void)! = {}) {
|
||||
public func showText(_ text: String, withComplection completion: (() -> Void)! = {}) {
|
||||
let baseText = self.titleLabel?.text ?? " "
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
self.titleLabel?.alpha = 0
|
||||
@@ -97,12 +105,12 @@ extension UIButton {
|
||||
})
|
||||
}
|
||||
|
||||
func setAnimatableText(_ text: String, withComplection completion: (() -> Void)! = {}) {
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
public func setAnimatableText(_ text: String, withComplection completion: (() -> Void)! = {}) {
|
||||
SPAnimation.animate(0.3, animations: {
|
||||
self.titleLabel?.alpha = 0
|
||||
}, withComplection: {
|
||||
self.setTitle(text, for: .normal)
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
SPAnimation.animate(0.3, animations: {
|
||||
self.titleLabel?.alpha = 1
|
||||
}, withComplection: {
|
||||
completion()
|
||||
@@ -110,16 +118,16 @@ extension UIButton {
|
||||
})
|
||||
}
|
||||
|
||||
func hideContent(completion: (() -> Void)! = {}) {
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
public func hideContent(completion: (() -> Void)! = {}) {
|
||||
SPAnimation.animate(0.25, animations: {
|
||||
self.titleLabel?.alpha = 0
|
||||
}, withComplection: {
|
||||
completion()
|
||||
})
|
||||
}
|
||||
|
||||
func showContent(completion: (() -> Void)! = {}) {
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
public func showContent(completion: (() -> Void)! = {}) {
|
||||
SPAnimation.animate(0.25, animations: {
|
||||
self.titleLabel?.alpha = 1
|
||||
}, withComplection: {
|
||||
completion()
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
extension UICollectionView {
|
||||
|
||||
var currentIndexCellPath: IndexPath? {
|
||||
public 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)
|
||||
|
||||
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
public extension UIColor {
|
||||
|
||||
convenience init(hex: String) {
|
||||
public convenience init(hex: String) {
|
||||
var red: CGFloat = 0.0
|
||||
var green: CGFloat = 0.0
|
||||
var blue: CGFloat = 0.0
|
||||
|
||||
@@ -23,89 +23,39 @@ 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(weight: FontWeight, size: CGFloat) -> UIFont {
|
||||
return UIFont.systemFont(ofSize: size, weight: self.weight(for: weight))
|
||||
}
|
||||
|
||||
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:
|
||||
private static func weight(for weight: FontWeight) -> UIFont.Weight {
|
||||
switch weight {
|
||||
case .ultraLight:
|
||||
return UIFont.Weight.ultraLight
|
||||
case .Light:
|
||||
case .light:
|
||||
return UIFont.Weight.light
|
||||
case .Medium:
|
||||
case .medium:
|
||||
return UIFont.Weight.medium
|
||||
case .Regular:
|
||||
case .regular:
|
||||
return UIFont.Weight.regular
|
||||
case .Bold:
|
||||
case .bold:
|
||||
return UIFont.Weight.bold
|
||||
case .DemiBold:
|
||||
case .demiBold:
|
||||
return UIFont.Weight.semibold
|
||||
case .Heavy:
|
||||
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
|
||||
public enum FontWeight {
|
||||
case regular
|
||||
case medium
|
||||
case light
|
||||
case ultraLight
|
||||
case heavy
|
||||
case bold
|
||||
case demiBold
|
||||
case none
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,4 +32,12 @@ public extension UIImage {
|
||||
UIGraphicsEndImageContext()
|
||||
return newImage!
|
||||
}
|
||||
|
||||
var alwaysOriginal: UIImage {
|
||||
return self.withRenderingMode(.alwaysOriginal)
|
||||
}
|
||||
|
||||
var alwaysTemplate: UIImage {
|
||||
return self.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ extension UIImageView {
|
||||
|
||||
public func setNative() {
|
||||
self.layer.borderWidth = 0.5
|
||||
self.layer.borderColor = SPNativeStyleKit.Colors.midGray.cgColor
|
||||
self.layer.borderColor = SPNativeColors.midGray.cgColor
|
||||
self.layer.masksToBounds = true
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
public extension UILabel {
|
||||
|
||||
func setShadowOffsetForLetters(blurRadius: CGFloat = 0, widthOffset: CGFloat = 0, heightOffset: CGFloat, opacity: CGFloat) {
|
||||
public func setShadowOffsetForLetters(blurRadius: CGFloat = 0, widthOffset: CGFloat = 0, heightOffset: CGFloat, opacity: CGFloat) {
|
||||
self.layer.shadowRadius = blurRadius
|
||||
self.layer.shadowOffset = CGSize(
|
||||
width: widthOffset,
|
||||
@@ -32,25 +32,45 @@ public extension UILabel {
|
||||
self.layer.shadowOpacity = Float(opacity)
|
||||
}
|
||||
|
||||
func setShadowOffsetFactorForLetters(blurRadius: CGFloat = 0, widthOffsetFactor: CGFloat = 0, heightOffsetFactor: CGFloat, opacity: CGFloat) {
|
||||
public 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() {
|
||||
public func removeShadowForLetters() {
|
||||
self.setShadowOffsetForLetters(blurRadius: 0, widthOffset: 0, heightOffset: 0, opacity: 0)
|
||||
}
|
||||
|
||||
func setCenteringAlignment() {
|
||||
public func setCenterAlignment() {
|
||||
self.textAlignment = .center
|
||||
self.baselineAdjustment = .alignCenters
|
||||
}
|
||||
|
||||
func setLettersSpacing(_ value: CGFloat) {
|
||||
public func setLettersSpacing(_ value: CGFloat) {
|
||||
if let textString = text {
|
||||
let attrs: [NSAttributedString.Key : Any] = [.kern: value]
|
||||
attributedText = NSAttributedString(string: textString, attributes: attrs)
|
||||
}
|
||||
}
|
||||
|
||||
public func setLineSpacing(_ lineSpacing: CGFloat = 0.0, lineHeightMultiple: CGFloat = 0.0) {
|
||||
|
||||
guard let labelText = self.text else { return }
|
||||
|
||||
let paragraphStyle = NSMutableParagraphStyle()
|
||||
paragraphStyle.lineSpacing = lineSpacing
|
||||
paragraphStyle.lineHeightMultiple = lineHeightMultiple
|
||||
|
||||
let attributedString:NSMutableAttributedString
|
||||
if let labelattributedText = self.attributedText {
|
||||
attributedString = NSMutableAttributedString(attributedString: labelattributedText)
|
||||
} else {
|
||||
attributedString = NSMutableAttributedString(string: labelText)
|
||||
}
|
||||
|
||||
attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attributedString.length))
|
||||
|
||||
self.attributedText = attributedString
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -23,12 +23,12 @@ import UIKit
|
||||
|
||||
extension UINavigationController {
|
||||
|
||||
static var elementsColor: UIColor {
|
||||
public static var elementsColor: UIColor {
|
||||
get {
|
||||
if UINavigationBar.appearance().tintColor != nil {
|
||||
return UINavigationBar.appearance().tintColor
|
||||
} else {
|
||||
return SPNativeStyleKit.Colors.blue
|
||||
return SPNativeColors.blue
|
||||
}
|
||||
}
|
||||
set {
|
||||
|
||||
+14
-1
@@ -23,7 +23,20 @@ import UIKit
|
||||
|
||||
extension UITabBarController {
|
||||
|
||||
func addTabBarItem(title: String, image: UIImage, selectedImage: UIImage? = nil, controller: UIViewController) {
|
||||
public static var elementsColor: UIColor {
|
||||
get {
|
||||
if UITabBar.appearance().tintColor != nil {
|
||||
return UITabBar.appearance().tintColor
|
||||
} else {
|
||||
return SPNativeColors.blue
|
||||
}
|
||||
}
|
||||
set {
|
||||
UINavigationBar.appearance().tintColor = newValue
|
||||
}
|
||||
}
|
||||
|
||||
public func addTabBarItem(title: String, image: UIImage, selectedImage: UIImage? = nil, controller: UIViewController) {
|
||||
|
||||
let tabBarItem = UITabBarItem(
|
||||
title: title,
|
||||
|
||||
@@ -23,19 +23,19 @@ import UIKit
|
||||
|
||||
extension UITableView {
|
||||
|
||||
var isEmpty: Bool {
|
||||
public var isEmpty: Bool {
|
||||
return self.lastSectionWithRows == nil
|
||||
}
|
||||
|
||||
func isEmpty(section: Int) -> Bool {
|
||||
public func isEmpty(section: Int) -> Bool {
|
||||
return self.numberOfRows(inSection: section) == 0
|
||||
}
|
||||
|
||||
var lastSection: Int {
|
||||
public var lastSection: Int {
|
||||
return self.numberOfSections - 1
|
||||
}
|
||||
|
||||
var lastSectionWithRows: Int? {
|
||||
public var lastSectionWithRows: Int? {
|
||||
if self.numberOfSections == 0 { return nil }
|
||||
var section = self.numberOfSections - 1
|
||||
if section < 0 { return nil }
|
||||
@@ -46,7 +46,7 @@ extension UITableView {
|
||||
return nil
|
||||
}
|
||||
|
||||
var firstSectionWithRows: Int? {
|
||||
public var firstSectionWithRows: Int? {
|
||||
if self.numberOfSections == 0 { return nil }
|
||||
var section = 0
|
||||
if section > self.numberOfSections - 1 { return nil }
|
||||
|
||||
+2
-2
@@ -27,13 +27,13 @@ extension UITableViewCell {
|
||||
return subviews.compactMap { $0 as? UIButton }.first
|
||||
}
|
||||
|
||||
var highlightedColor: UIColor? {
|
||||
public var highlightedColor: UIColor? {
|
||||
get {
|
||||
return self.backgroundView?.backgroundColor
|
||||
}
|
||||
set {
|
||||
let backgroundView = UIView()
|
||||
backgroundView.backgroundColor = SPNativeStyleKit.Colors.customGray
|
||||
backgroundView.backgroundColor = SPNativeColors.customGray
|
||||
self.selectedBackgroundView = backgroundView
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
extension UITextField {
|
||||
|
||||
var isEmptyText: Bool {
|
||||
public var isEmptyText: Bool {
|
||||
get {
|
||||
if self.text == "" {
|
||||
return true
|
||||
|
||||
+15
-15
@@ -24,15 +24,15 @@ import Photos
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
func present(_ viewControllerToPresent: UIViewController, completion: (() -> Swift.Void)? = nil) {
|
||||
public func present(_ viewControllerToPresent: UIViewController, completion: (() -> Swift.Void)? = nil) {
|
||||
self.present(viewControllerToPresent, animated: true, completion: completion)
|
||||
}
|
||||
|
||||
@objc func dismiss() {
|
||||
@objc public func dismiss() {
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func wrapToNavigationController(statusBar: SPStatusBar = .dark) -> UINavigationController {
|
||||
public func wrapToNavigationController(statusBar: SPStatusBar = .dark) -> UINavigationController {
|
||||
let controller = SPStatusBarManagerNavigationController(rootViewController: self)
|
||||
controller.statusBar = statusBar
|
||||
return controller
|
||||
@@ -41,20 +41,20 @@ extension UIViewController {
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
func dismissKeyboardWhenTappedAround() {
|
||||
public func dismissKeyboardWhenTappedAround() {
|
||||
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
|
||||
tap.cancelsTouchesInView = false
|
||||
view.addGestureRecognizer(tap)
|
||||
}
|
||||
|
||||
@objc func dismissKeyboard() {
|
||||
@objc public func dismissKeyboard() {
|
||||
view.endEditing(true)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
func save(image: UIImage) {
|
||||
public func save(image: UIImage) {
|
||||
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized {
|
||||
UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.image(_:didFinishSavingWithError:contextInfo:)), nil)
|
||||
} else {
|
||||
@@ -62,7 +62,7 @@ extension UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
func saveVideo(url: String, complection: @escaping (Bool)->()) {
|
||||
public func saveVideo(url: String, complection: @escaping (Bool)->()) {
|
||||
DispatchQueue.global(qos: .utility).async {
|
||||
let urls = URL(string: url)
|
||||
let urldata = try? Data(contentsOf: urls!)
|
||||
@@ -90,7 +90,7 @@ extension UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
@objc func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
|
||||
@objc public func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
|
||||
if let _ = error {
|
||||
self.imageSaved(isSuccses: false)
|
||||
} else {
|
||||
@@ -98,14 +98,14 @@ extension UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
@objc func imageSaved(isSuccses: Bool) {
|
||||
@objc public func imageSaved(isSuccses: Bool) {
|
||||
fatalError("SPUIViewControllerExtenshion - Need ovveride 'imageSaved' func")
|
||||
}
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
func setPrefersLargeNavigationTitle(_ title: String, smallScreenToSmallBar: Bool = true) {
|
||||
public func setPrefersLargeNavigationTitle(_ title: String, smallScreenToSmallBar: Bool = true) {
|
||||
self.navigationItem.title = title
|
||||
if #available(iOS 11.0, *) {
|
||||
self.navigationItem.largeTitleDisplayMode = .automatic
|
||||
@@ -119,7 +119,7 @@ extension UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
func setNavigationTitle(_ title: String, style: SPNavigationTitleStyle) {
|
||||
public func setNavigationTitle(_ title: String, style: SPNavigationTitleStyle) {
|
||||
self.navigationItem.title = title
|
||||
switch style {
|
||||
case .large:
|
||||
@@ -141,7 +141,7 @@ extension UIViewController {
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
var safeArea: UIEdgeInsets {
|
||||
public var safeArea: UIEdgeInsets {
|
||||
if #available(iOS 11.0, *) {
|
||||
return self.view.safeAreaInsets
|
||||
} else {
|
||||
@@ -149,18 +149,18 @@ extension UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
var navigationBarHeight: CGFloat {
|
||||
public var navigationBarHeight: CGFloat {
|
||||
return self.navigationController?.navigationBar.frame.height ?? 0
|
||||
}
|
||||
|
||||
static var statusBarHeight: CGFloat {
|
||||
public static var statusBarHeight: CGFloat {
|
||||
return UIApplication.shared.statusBarFrame.height
|
||||
}
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
var navigationTitleColor: UIColor? {
|
||||
public var navigationTitleColor: UIColor? {
|
||||
get {
|
||||
return (self.navigationController?.navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor) ?? nil
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
public extension UIView {
|
||||
|
||||
var viewController: UIViewController? {
|
||||
public var viewController: UIViewController? {
|
||||
get {
|
||||
if let nextResponder = self.next as? UIViewController { return nextResponder }
|
||||
else if let nextResponder = self.next as? UIView { return nextResponder.viewController }
|
||||
@@ -34,35 +34,19 @@ public extension UIView {
|
||||
|
||||
public extension UIView {
|
||||
|
||||
var topSafeArea: CGFloat {
|
||||
var topSafeArea: CGFloat = 0
|
||||
public var safeArea: UIEdgeInsets {
|
||||
if #available(iOS 11.0, *) {
|
||||
topSafeArea = self.safeAreaInsets.top
|
||||
return self.safeAreaInsets
|
||||
} else{
|
||||
return UIEdgeInsets.zero
|
||||
}
|
||||
return topSafeArea
|
||||
}
|
||||
|
||||
var bottomSafeArea: CGFloat {
|
||||
var bottomSafeArea: CGFloat = 0
|
||||
if #available(iOS 11.0, *) {
|
||||
bottomSafeArea = self.safeAreaInsets.bottom
|
||||
}
|
||||
return bottomSafeArea
|
||||
public func setBounds(_ view: UIView, withWidthFactor widthFactor: CGFloat = 1, maxWidth: CGFloat? = nil, withHeightFactor heightFactor: CGFloat = 1, maxHeight: CGFloat? = nil, withCentering: Bool = false) {
|
||||
self.setBounds(view.bounds, withWidthFactor: widthFactor, maxWidth: maxWidth, withHeightFactor: heightFactor, maxHeight: maxHeight, withCentering: withCentering)
|
||||
}
|
||||
|
||||
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) {
|
||||
public func setBounds(_ 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!) }
|
||||
@@ -78,18 +62,18 @@ public extension UIView {
|
||||
}
|
||||
}
|
||||
|
||||
func setEqualsBoundsFromSuperview(customWidth: CGFloat? = nil, customHeight: CGFloat? = nil) {
|
||||
public func setSuperviewBounds(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))
|
||||
self.frame = CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: customWidth!, height: self.superview!.frame.height))
|
||||
}
|
||||
if customHeight != nil {
|
||||
self.frame = CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: self.frame.width, height: customHeight!))
|
||||
self.frame = CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: self.superview!.frame.width, height: customHeight!))
|
||||
}
|
||||
}
|
||||
|
||||
func resize(width: CGFloat) {
|
||||
public func resize(width: CGFloat) {
|
||||
let relativeFactor = self.frame.width / self.frame.height
|
||||
if relativeFactor.isNaN { return }
|
||||
self.frame = CGRect.init(
|
||||
@@ -100,7 +84,7 @@ public extension UIView {
|
||||
)
|
||||
}
|
||||
|
||||
func resize(height: CGFloat) {
|
||||
public func resize(height: CGFloat) {
|
||||
let relativeFactor = self.frame.width / self.frame.height
|
||||
if relativeFactor.isNaN { return }
|
||||
self.frame = CGRect.init(
|
||||
@@ -111,27 +95,27 @@ public extension UIView {
|
||||
)
|
||||
}
|
||||
|
||||
func setYCenteringFromSuperview() {
|
||||
public func setYCenter() {
|
||||
self.center.y = (self.superview?.frame.height ?? 0) / 2
|
||||
}
|
||||
|
||||
func setXCenteringFromSuperview() {
|
||||
public func setXCenter() {
|
||||
self.center.x = (self.superview?.frame.width ?? 0) / 2
|
||||
}
|
||||
|
||||
func setToCenterInSuperview() {
|
||||
public func setToCenter() {
|
||||
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
|
||||
public func setParalax(amountFactor: CGFloat) {
|
||||
let amount = self.frame.minSide * amountFactor
|
||||
self.setParalax(amount: amount)
|
||||
}
|
||||
|
||||
func setParalax(amount: CGFloat) {
|
||||
public func setParalax(amount: CGFloat) {
|
||||
self.motionEffects.removeAll()
|
||||
let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis)
|
||||
horizontal.minimumRelativeValue = -amount
|
||||
@@ -149,11 +133,11 @@ public extension UIView {
|
||||
|
||||
public extension UIView {
|
||||
|
||||
func addGrade(alpha: CGFloat, color: UIColor = UIColor.black) -> UIView {
|
||||
public func addGrade(alpha: CGFloat, color: UIColor = UIColor.black) -> UIView {
|
||||
let gradeView = UIView.init()
|
||||
gradeView.alpha = 0
|
||||
self.addSubview(gradeView)
|
||||
SPConstraints.setEqualSize(gradeView, superVuew: self)
|
||||
SPConstraints.setEqualSizeSuperview(for: gradeView)
|
||||
gradeView.alpha = alpha
|
||||
gradeView.backgroundColor = color
|
||||
return gradeView
|
||||
@@ -162,7 +146,7 @@ public extension UIView {
|
||||
|
||||
extension UIView {
|
||||
|
||||
func setShadow(
|
||||
public func setShadow(
|
||||
xTranslationFactor: CGFloat,
|
||||
yTranslationFactor: CGFloat,
|
||||
widthRelativeFactor: CGFloat,
|
||||
@@ -177,14 +161,14 @@ extension UIView {
|
||||
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 cornerRadius = self.frame.minSide * cornerRadiusFactor
|
||||
|
||||
let shadowPath = UIBezierPath.init(
|
||||
roundedRect: CGRect.init(x: xTranslation, y: yTranslation, width: shadowWidth, height: shadowHeight),
|
||||
cornerRadius: cornerRadius
|
||||
)
|
||||
|
||||
let blurRadius = self.frame.minSideSize * blurRadiusFactor
|
||||
let blurRadius = self.frame.minSide * blurRadiusFactor
|
||||
|
||||
self.layer.shadowColor = UIColor.black.cgColor
|
||||
self.layer.shadowOffset = CGSize.zero
|
||||
@@ -194,7 +178,7 @@ extension UIView {
|
||||
self.layer.shadowPath = shadowPath.cgPath;
|
||||
}
|
||||
|
||||
func setShadow(
|
||||
public func setShadow(
|
||||
xTranslation: CGFloat,
|
||||
yTranslation: CGFloat,
|
||||
widthRelativeFactor: CGFloat,
|
||||
@@ -219,14 +203,14 @@ extension UIView {
|
||||
self.layer.shadowPath = shadowPath.cgPath
|
||||
}
|
||||
|
||||
func removeShadow() {
|
||||
public 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) {
|
||||
public func addShadowOpacityAnimation(to: CGFloat, duration: CFTimeInterval) {
|
||||
let animation = CABasicAnimation(keyPath:"shadowOpacity")
|
||||
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
|
||||
animation.fromValue = self.layer.cornerRadius
|
||||
@@ -240,7 +224,7 @@ extension UIView {
|
||||
|
||||
extension UIView {
|
||||
|
||||
func addCornerRadiusAnimation(to: CGFloat, duration: CFTimeInterval) {
|
||||
public func addCornerRadiusAnimation(to: CGFloat, duration: CFTimeInterval) {
|
||||
let animation = CABasicAnimation(keyPath:"cornerRadius")
|
||||
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
|
||||
animation.fromValue = self.layer.cornerRadius
|
||||
@@ -250,14 +234,14 @@ extension UIView {
|
||||
self.layer.cornerRadius = to
|
||||
}
|
||||
|
||||
func show(duration: TimeInterval = 0.3) {
|
||||
public func show(duration: TimeInterval = 0.3) {
|
||||
self.isHidden = false
|
||||
SPAnimation.animate(duration, animations: {
|
||||
self.alpha = 1
|
||||
})
|
||||
}
|
||||
|
||||
func hide(duration: TimeInterval = 0.3) {
|
||||
public func hide(duration: TimeInterval = 0.3) {
|
||||
SPAnimation.animate(duration, animations: {
|
||||
self.alpha = 0
|
||||
}, withComplection: {
|
||||
@@ -265,21 +249,21 @@ extension UIView {
|
||||
})
|
||||
}
|
||||
|
||||
func removeAllAnimations() {
|
||||
public func removeAllAnimations() {
|
||||
self.layer.removeAllAnimations()
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
|
||||
func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
|
||||
public 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
|
||||
public func round() {
|
||||
self.layer.cornerRadius = self.frame.minSide / 2
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -23,12 +23,12 @@ import UIKit
|
||||
|
||||
extension UIVisualEffectView {
|
||||
|
||||
convenience init(style: UIBlurEffect.Style) {
|
||||
public convenience init(style: UIBlurEffect.Style) {
|
||||
let effect = UIBlurEffect(style: style)
|
||||
self.init(effect: effect)
|
||||
}
|
||||
|
||||
convenience init(vibrancy style: UIBlurEffect.Style) {
|
||||
public convenience init(vibrancy style: UIBlurEffect.Style) {
|
||||
let effect = UIBlurEffect(style: style)
|
||||
let vibrancyEffect = UIVibrancyEffect(blurEffect: effect)
|
||||
self.init(effect: vibrancyEffect)
|
||||
|
||||
+3
-3
@@ -23,15 +23,15 @@ import Foundation
|
||||
|
||||
extension UserDefaults {
|
||||
|
||||
func set(stringArray array: [String], forKey key: String) {
|
||||
public func set(stringArray array: [String], forKey key: String) {
|
||||
self.set(array, forKey: key)
|
||||
}
|
||||
|
||||
func set(boolArray array: [Bool], forKey key: String) {
|
||||
public func set(boolArray array: [Bool], forKey key: String) {
|
||||
self.set(array, forKey: key)
|
||||
}
|
||||
|
||||
func boolArray(forKey defaultName: String) -> [Bool] {
|
||||
public func boolArray(forKey defaultName: String) -> [Bool] {
|
||||
return UserDefaults.standard.array(forKey: defaultName) as? [Bool] ?? []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,22 +21,32 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
struct SPLayout {
|
||||
public struct SPLayout {
|
||||
|
||||
static func sizeWith(widthFactor: CGFloat, maxWidth: CGFloat, heightFactor: CGFloat, maxHeight: CGFloat, relativeSideFactor: CGFloat, from relativeSize: CGSize) -> CGSize {
|
||||
public static func sizeWith(widthFactor: CGFloat, maxWidth: CGFloat?, heightFactor: CGFloat, maxHeight: CGFloat?, relativeSideFactor: CGFloat?, from size: CGSize) -> CGSize {
|
||||
|
||||
var widthArea = relativeSize.width * widthFactor
|
||||
var heightArea = relativeSize.height * heightFactor
|
||||
var widthArea = size.width * widthFactor
|
||||
var heightArea = size.height * heightFactor
|
||||
|
||||
widthArea.setIfMore(when: maxWidth)
|
||||
heightArea.setIfMore(when: maxHeight)
|
||||
if let maxWidth = maxWidth {
|
||||
widthArea.setIfMore(when: maxWidth)
|
||||
}
|
||||
|
||||
if let maxHeight = maxHeight {
|
||||
heightArea.setIfMore(when: maxHeight)
|
||||
}
|
||||
|
||||
var prepareWidth = widthArea
|
||||
var prepareHeight = widthArea / relativeSideFactor
|
||||
if prepareHeight > heightArea {
|
||||
prepareHeight = heightArea
|
||||
prepareWidth = heightArea * relativeSideFactor
|
||||
var prepareHeight = heightArea
|
||||
|
||||
if let relativeSideFactor = relativeSideFactor {
|
||||
prepareHeight = widthArea / relativeSideFactor
|
||||
if prepareHeight > heightArea {
|
||||
prepareHeight = heightArea
|
||||
prepareWidth = heightArea * relativeSideFactor
|
||||
}
|
||||
}
|
||||
|
||||
return CGSize.init(width: prepareWidth, height: prepareHeight)
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ import LocalAuthentication
|
||||
|
||||
struct SPLocalAuthentication {
|
||||
|
||||
static var isEnable: Bool {
|
||||
public static var isEnable: Bool {
|
||||
let context = LAContext()
|
||||
var error: NSError?
|
||||
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
|
||||
@@ -38,7 +38,7 @@ struct SPLocalAuthentication {
|
||||
}
|
||||
}
|
||||
|
||||
static func request(reason: String, complecton: @escaping (Bool)->()) {
|
||||
public static func request(reason: String, complecton: @escaping (Bool)->()) {
|
||||
let context = LAContext()
|
||||
var error: NSError?
|
||||
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
enum SPLocale: String, CaseIterable {
|
||||
public enum SPLocale: String, CaseIterable {
|
||||
|
||||
case ru = "ru"
|
||||
case en = "en"
|
||||
|
||||
@@ -24,11 +24,11 @@ import MessageUI
|
||||
|
||||
struct SPMail {
|
||||
|
||||
static var canSendEmail: Bool {
|
||||
public static var canSendEmail: Bool {
|
||||
return MFMailComposeViewController.canSendMail()
|
||||
}
|
||||
|
||||
static func openMailApp(to email: String, subject: String? = nil, body: String? = nil) {
|
||||
public static func openApp(to email: String, subject: String? = nil, body: String? = nil) {
|
||||
let parametrs = "mailto:\(email)?subject=\(subject ?? "")&body=\(body ?? "")".addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
|
||||
|
||||
if parametrs != nil {
|
||||
@@ -40,7 +40,7 @@ struct SPMail {
|
||||
}
|
||||
}
|
||||
|
||||
static func mailDialog(to email: String, subject: String? = nil, body: String? = nil, on viewController: UIViewController) {
|
||||
public static func dialog(to email: String, subject: String? = nil, body: String? = nil, on viewController: UIViewController) {
|
||||
let mailVC = MFMailComposeViewController()
|
||||
mailVC.mailComposeDelegate = SPMailSingltone.sharedInstance
|
||||
|
||||
@@ -69,7 +69,6 @@ struct SPMail {
|
||||
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,41 @@
|
||||
// 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 SPNativeColors {
|
||||
|
||||
public static let red = UIColor.init(hex: "FF3B30")
|
||||
public static let orange = UIColor.init(hex: "FF9500")
|
||||
public static let yellow = UIColor.init(hex: "FFCC00")
|
||||
public static let green = UIColor.init(hex: "4CD964")
|
||||
public static let tealBlue = UIColor.init(hex: "5AC8FA")
|
||||
public static let blue = UIColor.init(hex: "007AFF")
|
||||
public static let purple = UIColor.init(hex: "5856D6")
|
||||
public static let pink = UIColor.init(hex: "FF2D55")
|
||||
public static let white = UIColor.init(hex: "FFFFFF")
|
||||
public static let customGray = UIColor.init(hex: "EFEFF4")
|
||||
public static let lightGray = UIColor.init(hex: "E5E5EA")
|
||||
public static let lightGray2 = UIColor.init(hex: "D1D1D6")
|
||||
public static let midGray = UIColor.init(hex: "C7C7CC")
|
||||
public static let gray = UIColor.init(hex: "8E8E93")
|
||||
public static let black = UIColor.init(hex: "000000")
|
||||
}
|
||||
+7
-7
@@ -22,14 +22,14 @@
|
||||
import UIKit
|
||||
import UserNotifications
|
||||
|
||||
struct SPLocalNotification {
|
||||
public struct SPLocalNotification {
|
||||
|
||||
static func add(from timeInterval: TimeInterval, body: String, title: String? = nil, identifier: String? = nil) {
|
||||
public 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.badge = NSNumber(value: 1)
|
||||
content.sound = UNNotificationSound.default
|
||||
|
||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: false)
|
||||
@@ -50,12 +50,12 @@ struct SPLocalNotification {
|
||||
}
|
||||
}
|
||||
|
||||
static func add(in date: Date, body: String, title: String? = nil, identifier: String? = nil) {
|
||||
|
||||
public 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.badge = NSNumber(value: 1)
|
||||
content.sound = UNNotificationSound.default
|
||||
|
||||
let triggerDate = Calendar.current.dateComponents([.year,.month,.day,.hour,.minute,.second,], from: date)
|
||||
@@ -77,7 +77,7 @@ struct SPLocalNotification {
|
||||
}
|
||||
}
|
||||
|
||||
static func remove(identifier: String) {
|
||||
public static func remove(identifier: String) {
|
||||
let center = UNUserNotificationCenter.current()
|
||||
center.removePendingNotificationRequests(withIdentifiers: [identifier])
|
||||
}
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
// 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)})
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
import UIKit
|
||||
|
||||
public extension String {
|
||||
|
||||
public static func random(count: Int) -> String {
|
||||
let strings = [
|
||||
"В доме кардинала от меня не было тайн; не раз видел я, как он усердно перелистывает старинные книги и жадно роется в пыли фамильных рукописей. Когда я как-то упрекнул его за бесполезные бессонные ночи, после которых он впадал в болезненное уныние, он взглянул на меня с горькой улыбкой и раскрыл передо мною историю города Рима. В этой книге, в двадцатой главе жизнеописания папы Александра Шестого, я прочел следующие строки, навсегда оставшиеся в моей памяти",
|
||||
@@ -34,6 +35,7 @@ public extension String {
|
||||
}
|
||||
|
||||
public extension Int {
|
||||
|
||||
public static func random(_ n: Int) -> Int {
|
||||
return Int(arc4random_uniform(UInt32(n)))
|
||||
}
|
||||
@@ -44,6 +46,7 @@ public extension Int {
|
||||
}
|
||||
|
||||
public extension Double {
|
||||
|
||||
public static func random() -> Double {
|
||||
return Double(arc4random()) / 0xFFFFFFFF
|
||||
}
|
||||
@@ -54,6 +57,7 @@ public extension Double {
|
||||
}
|
||||
|
||||
public extension Float {
|
||||
|
||||
public static func random() -> Float {
|
||||
return Float(arc4random()) / 0xFFFFFFFF
|
||||
}
|
||||
@@ -64,6 +68,7 @@ public extension Float {
|
||||
}
|
||||
|
||||
public extension CGFloat {
|
||||
|
||||
public static func random() -> CGFloat {
|
||||
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
|
||||
}
|
||||
@@ -74,8 +79,8 @@ public extension CGFloat {
|
||||
}
|
||||
|
||||
public extension Collection {
|
||||
/// Return a copy of `self` with its elements shuffled
|
||||
func shuffle() -> [Iterator.Element] {
|
||||
|
||||
public func shuffle() -> [Iterator.Element] {
|
||||
var list = Array(self)
|
||||
list.shuffleInPlace()
|
||||
return list
|
||||
@@ -84,15 +89,15 @@ public extension Collection {
|
||||
|
||||
extension Collection where Index == Int {
|
||||
|
||||
func random() -> Iterator.Element? {
|
||||
public 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 {
|
||||
|
||||
@@ -21,4 +21,4 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct SPShadow { private init() {} }
|
||||
public struct SPShadow { private init() {} }
|
||||
|
||||
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
extension SPShadow {
|
||||
|
||||
struct DeepStyle {
|
||||
public struct DeepStyle {
|
||||
|
||||
private init() {}
|
||||
|
||||
@@ -66,7 +66,7 @@ extension SPShadow {
|
||||
yTranslation = view.frame.height + maxBottomSpace - shadowHeight
|
||||
}
|
||||
|
||||
var blurRadius = view.frame.minSideSize * blurRadiusFactor
|
||||
var blurRadius = view.frame.minSide * blurRadiusFactor
|
||||
blurRadius.setIfMore(when: 10)
|
||||
blurRadius.setIfFewer(when: 7)
|
||||
|
||||
@@ -85,14 +85,14 @@ extension SPShadow {
|
||||
|
||||
extension UIView {
|
||||
|
||||
func setDeepShadow() {
|
||||
public func setDeepShadow() {
|
||||
SPShadow.DeepStyle.setFor(view: self)
|
||||
}
|
||||
}
|
||||
|
||||
extension UILabel {
|
||||
|
||||
func setDeepShadowForLetters() {
|
||||
public func setDeepShadowForLetters() {
|
||||
SPShadow.DeepStyle.setFor(label: self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ 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) {
|
||||
public 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 {
|
||||
|
||||
@@ -21,42 +21,37 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPInstagram {
|
||||
public class SPInstagram {
|
||||
|
||||
static var isSetApp: Bool {
|
||||
if UIApplication.shared.canOpenURL(URL(string: "instagram://user?username=test")!) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
public static var isSetApp: Bool {
|
||||
return UIApplication.shared.canOpenURL(URL(string: "instagram://user?username=test")!)
|
||||
}
|
||||
|
||||
static func openPost(id: String) {
|
||||
public 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)
|
||||
SPApp.open(link: safariURL.absoluteString, redirect: true)
|
||||
}
|
||||
}
|
||||
|
||||
static func openUser(username: String) {
|
||||
public 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)
|
||||
SPApp.open(link: safariURL.absoluteString, redirect: true)
|
||||
}
|
||||
}
|
||||
|
||||
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)})
|
||||
}
|
||||
|
||||
@@ -21,17 +21,13 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPTelegram {
|
||||
public class SPTelegram {
|
||||
|
||||
static var isSetApp: Bool {
|
||||
if UIApplication.shared.canOpenURL(URL(string: "tg://msg?text=test")!) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
public static var isSetApp: Bool {
|
||||
return UIApplication.shared.canOpenURL(URL(string: "tg://msg?text=test")!)
|
||||
}
|
||||
|
||||
static func share(text: String, complection: @escaping (_ isOpened: Bool)->() = {_ in }) {
|
||||
public 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 {
|
||||
@@ -45,24 +41,23 @@ class SPTelegram {
|
||||
}
|
||||
}
|
||||
|
||||
static func joinChannel(id: String) {
|
||||
public static func joinChannel(id: String) {
|
||||
let url = "https://t.me/joinchat/\(id)"
|
||||
SPOpener.Link.redirectToBrowserAndOpen(link: url)
|
||||
SPApp.open(link: url, redirect: true)
|
||||
}
|
||||
|
||||
static func openBot(username: String) {
|
||||
public static func openBot(username: String) {
|
||||
var username = username
|
||||
if username.first == "@" {
|
||||
username.removeFirst()
|
||||
}
|
||||
let url = "https://telegram.me/\(username)"
|
||||
SPOpener.Link.redirectToBrowserAndOpen(link: url)
|
||||
SPApp.open(link: url, redirect: true)
|
||||
}
|
||||
|
||||
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)})
|
||||
}
|
||||
|
||||
@@ -21,17 +21,13 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPTwitter {
|
||||
public class SPTwitter {
|
||||
|
||||
static var isSetApp: Bool {
|
||||
if UIApplication.shared.canOpenURL(URL(string: "twitter://post?message=test")!) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
public static var isSetApp: Bool {
|
||||
return UIApplication.shared.canOpenURL(URL(string: "twitter://post?message=test")!)
|
||||
}
|
||||
|
||||
static func share(text: String, complection: @escaping (_ isOpened: Bool)->() = {_ in }) {
|
||||
public 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 {
|
||||
@@ -48,7 +44,6 @@ class SPTwitter {
|
||||
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)})
|
||||
}
|
||||
|
||||
@@ -21,17 +21,13 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPViber {
|
||||
public class SPViber {
|
||||
|
||||
static var isSetApp: Bool {
|
||||
if UIApplication.shared.canOpenURL(URL(string: "viber://forward?text=test")!) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
public static var isSetApp: Bool {
|
||||
return UIApplication.shared.canOpenURL(URL(string: "viber://forward?text=test")!)
|
||||
}
|
||||
|
||||
static func share(text: String, complection: @escaping (_ isOpened: Bool)->() = {_ in }) {
|
||||
public 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 {
|
||||
@@ -48,9 +44,6 @@ class SPViber {
|
||||
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)})
|
||||
}
|
||||
|
||||
@@ -21,17 +21,13 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPWhatsApp {
|
||||
public class SPWhatsApp {
|
||||
|
||||
static var isSetApp: Bool {
|
||||
if UIApplication.shared.canOpenURL(URL(string: "whatsapp://send?text=test")!) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
public static var isSetApp: Bool {
|
||||
return UIApplication.shared.canOpenURL(URL(string: "whatsapp://send?text=test")!)
|
||||
}
|
||||
|
||||
static func share(text: String, complection: @escaping (_ isOpened: Bool)->() = {_ in }) {
|
||||
public 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 {
|
||||
@@ -48,7 +44,6 @@ class SPWhatsApp {
|
||||
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)})
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
// 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() {}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
// 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() {}
|
||||
}
|
||||
@@ -26,12 +26,7 @@ public enum SPStatusBar {
|
||||
case light
|
||||
}
|
||||
|
||||
public enum SPSnapToSide {
|
||||
case left
|
||||
case right
|
||||
}
|
||||
|
||||
public enum SPSystemIconType {
|
||||
public enum SPSystemIcon {
|
||||
case share
|
||||
case close
|
||||
case favorite
|
||||
@@ -73,3 +68,8 @@ public enum SPSystemApp {
|
||||
case photos
|
||||
case setting
|
||||
}
|
||||
|
||||
public enum SPSelectionType {
|
||||
case select
|
||||
case unselect
|
||||
}
|
||||
|
||||
+7
-7
@@ -21,7 +21,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPAppStoreActionButton: SPDownloadingButton {
|
||||
public class SPAppStoreActionButton: SPDownloadingButton {
|
||||
|
||||
var style: Style = .base {
|
||||
didSet {
|
||||
@@ -32,28 +32,28 @@ class SPAppStoreActionButton: SPDownloadingButton {
|
||||
switch self.style {
|
||||
case .base:
|
||||
self.backgroundColor = self.secondColor
|
||||
self.titleLabel?.font = UIFont.system(type: .Bold, size: 14)
|
||||
self.titleLabel?.font = UIFont.system(weight: .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.titleLabel?.font = UIFont.system(weight: .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.titleLabel?.font = UIFont.system(weight: .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.titleLabel?.font = UIFont.system(weight: .medium, size: 14)
|
||||
self.contentEdgeInsets = UIEdgeInsets.init(top: 6, left: 15, bottom: 6, right: 15)
|
||||
break
|
||||
}
|
||||
@@ -78,7 +78,7 @@ class SPAppStoreActionButton: SPDownloadingButton {
|
||||
self.layer.masksToBounds = true
|
||||
}
|
||||
|
||||
override func setTitle(_ title: String?, for state: UIControl.State) {
|
||||
override public func setTitle(_ title: String?, for state: UIControl.State) {
|
||||
switch self.style {
|
||||
case .base:
|
||||
super.setTitle(title?.uppercased(), for: state)
|
||||
@@ -91,7 +91,7 @@ class SPAppStoreActionButton: SPDownloadingButton {
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
override public func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
if self.style == .buyInStore {
|
||||
self.layer.cornerRadius = 12
|
||||
|
||||
@@ -21,36 +21,36 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPAppleMusicButton: SPButton {
|
||||
public class SPAppleMusicButton: SPButton {
|
||||
|
||||
var mode: Mode = .unselect {
|
||||
var type: SPSelectionType = .unselect {
|
||||
didSet {
|
||||
self.updateStyle(animated: false)
|
||||
self.updateType(animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
var selectColor: UIColor = UIColor.init(hex: "FD2D55") {
|
||||
didSet {
|
||||
self.updateStyle(animated: false)
|
||||
self.updateType(animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
var baseColor: UIColor = UIColor.init(hex: "F8F7FC") {
|
||||
didSet {
|
||||
self.updateStyle(animated: false)
|
||||
self.updateType(animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
override func commonInit() {
|
||||
super.commonInit()
|
||||
self.layer.cornerRadius = 8
|
||||
self.titleLabel?.font = UIFont.system(type: .DemiBold, size: 15)
|
||||
self.titleLabel?.font = UIFont.system(weight: .demiBold, size: 15)
|
||||
self.contentEdgeInsets = UIEdgeInsets.init(top: 12, left: 27, bottom: 12, right: 27)
|
||||
self.mode = .unselect
|
||||
self.type = .unselect
|
||||
}
|
||||
|
||||
private func updateStyle(animated: Bool) {
|
||||
switch self.mode {
|
||||
private func updateType(animated: Bool) {
|
||||
switch self.type {
|
||||
case .select:
|
||||
self.backgroundColor = self.selectColor
|
||||
self.setTitleColor(UIColor.white)
|
||||
@@ -61,10 +61,5 @@ class SPAppleMusicButton: SPButton {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
case select
|
||||
case unselect
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
// 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 SPAppleMusicSectionButtonsView: SPView {
|
||||
|
||||
let topSeparatorView = SPSeparatorView()
|
||||
let bottomSeparatorView = SPSeparatorView()
|
||||
let leftButton = SPAppleMusicButton()
|
||||
let rightButton = SPAppleMusicButton()
|
||||
|
||||
var sectionHeight: CGFloat = 92 {
|
||||
didSet { self.layoutSubviews() }
|
||||
}
|
||||
|
||||
var buttonsSpace: CGFloat = 18 {
|
||||
didSet { self.layoutSubviews() }
|
||||
}
|
||||
|
||||
var selectColor: UIColor = UIColor.init(hex: "FD2D55") {
|
||||
didSet {
|
||||
self.leftButton.selectColor = selectColor
|
||||
self.rightButton.selectColor = selectColor
|
||||
}
|
||||
}
|
||||
|
||||
var baseColor: UIColor = UIColor.init(hex: "F8F7FC") {
|
||||
didSet {
|
||||
self.leftButton.baseColor = baseColor
|
||||
self.rightButton.baseColor = baseColor
|
||||
}
|
||||
}
|
||||
|
||||
override func commonInit() {
|
||||
super.commonInit()
|
||||
self.addSubview(self.topSeparatorView)
|
||||
self.addSubview(self.bottomSeparatorView)
|
||||
|
||||
for button in [self.leftButton, self.rightButton] {
|
||||
button.type = .unselect
|
||||
button.setTitle("Title")
|
||||
self.addSubview(button)
|
||||
}
|
||||
}
|
||||
|
||||
func layout(origin: CGPoint, width: CGFloat) {
|
||||
self.frame.origin = origin
|
||||
self.frame.set(width: width, height: sectionHeight)
|
||||
self.layoutSubviews()
|
||||
}
|
||||
|
||||
override public func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.topSeparatorView.frame.origin = .zero
|
||||
self.topSeparatorView.frame.set(width: self.frame.width)
|
||||
|
||||
self.bottomSeparatorView.frame.origin.x = 0
|
||||
self.bottomSeparatorView.frame.bottomY = self.frame.height
|
||||
self.bottomSeparatorView.frame.set(width: self.frame.width)
|
||||
|
||||
let buttonWidth = (self.frame.width - self.buttonsSpace) / 2
|
||||
|
||||
self.leftButton.sizeToFit()
|
||||
self.leftButton.frame.set(width: buttonWidth)
|
||||
self.leftButton.frame.origin.x = 0
|
||||
self.leftButton.center.y = self.frame.height / 2
|
||||
|
||||
self.rightButton.sizeToFit()
|
||||
self.rightButton.frame.set(width: buttonWidth)
|
||||
self.rightButton.frame.bottomX = self.frame.width
|
||||
self.rightButton.center.y = self.frame.height / 2
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,28 @@ import UIKit
|
||||
|
||||
public class SPButton: UIButton {
|
||||
|
||||
var gradientView: SPGradientView? {
|
||||
override public func imageRect(forContentRect contentRect: CGRect) -> CGRect {
|
||||
if self.title(for: .normal) != nil {
|
||||
let inset: CGFloat = 6
|
||||
let sideSize = self.frame.height - inset * 2
|
||||
let titleFrame = self.titleRect(forContentRect: contentRect)
|
||||
return CGRect.init(x: titleFrame.origin.x - sideSize - 6, y: 0, width: sideSize, height: self.frame.height)
|
||||
} else {
|
||||
return super.imageRect(forContentRect: contentRect)
|
||||
}
|
||||
}
|
||||
|
||||
override public var isHighlighted: Bool{
|
||||
didSet {
|
||||
if self.isHighlighted {
|
||||
self.imageView?.alpha = 0.7
|
||||
} else {
|
||||
self.imageView?.alpha = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var gradientView: SPGradientView? {
|
||||
didSet {
|
||||
self.gradientView?.isUserInteractionEnabled = false
|
||||
if self.gradientView?.superview == nil {
|
||||
@@ -34,7 +55,7 @@ public class SPButton: UIButton {
|
||||
}
|
||||
}
|
||||
|
||||
var round: Bool = false {
|
||||
public var rounded: Bool = false {
|
||||
didSet {
|
||||
self.layoutSubviews()
|
||||
}
|
||||
@@ -50,13 +71,26 @@ public class SPButton: UIButton {
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
internal func commonInit() {}
|
||||
internal func commonInit() {
|
||||
self.adjustsImageWhenHighlighted = false
|
||||
}
|
||||
|
||||
override public func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.gradientView?.setEqualsBoundsFromSuperview()
|
||||
if self.round {
|
||||
self.gradientView?.setSuperviewBounds()
|
||||
if self.rounded {
|
||||
self.round()
|
||||
}
|
||||
}
|
||||
|
||||
public func set(enable: Bool, animatable: Bool) {
|
||||
self.isEnabled = enable
|
||||
if animatable {
|
||||
SPAnimation.animate(0.3, animations: {
|
||||
self.alpha = enable ? 1 : 0.6
|
||||
})
|
||||
} else {
|
||||
self.alpha = enable ? 1 : 0.6
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPDotButton: SPButton {
|
||||
public class SPDotButton: SPButton {
|
||||
|
||||
var customSideSize: CGFloat = 26 {
|
||||
didSet {
|
||||
@@ -37,7 +37,7 @@ class SPDotButton: SPButton {
|
||||
}
|
||||
}
|
||||
|
||||
override var isHighlighted: Bool{
|
||||
override public var isHighlighted: Bool{
|
||||
didSet{
|
||||
if isHighlighted{
|
||||
UIView.animate(withDuration: 0.1, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 1.0, options: [.curveEaseOut, .beginFromCurrentState], animations: {
|
||||
@@ -59,7 +59,7 @@ class SPDotButton: SPButton {
|
||||
|
||||
override func commonInit() {
|
||||
super.commonInit()
|
||||
self.backgroundColor = UIColor.black.withAlphaComponent(0.2)
|
||||
self.backgroundColor = UIColor.black.withAlphaComponent(0.5)
|
||||
for _ in 0...2 {
|
||||
let dotView = UIView()
|
||||
dotView.isUserInteractionEnabled = false
|
||||
@@ -69,14 +69,14 @@ class SPDotButton: SPButton {
|
||||
}
|
||||
}
|
||||
|
||||
override func sizeToFit() {
|
||||
override public func sizeToFit() {
|
||||
super.sizeToFit()
|
||||
self.setWidth(self.customSideSize)
|
||||
self.setHeight(self.customSideSize)
|
||||
self.frame.set(width: self.customSideSize)
|
||||
self.frame.set(height: self.customSideSize)
|
||||
self.layoutSubviews()
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
override public func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
let space: CGFloat = 2
|
||||
@@ -87,9 +87,9 @@ class SPDotButton: SPButton {
|
||||
var currentXPosition: CGFloat = insest
|
||||
|
||||
for dotView in self.dotsView {
|
||||
dotView.setWidth(sideSize)
|
||||
dotView.setHeight(sideSize)
|
||||
dotView.setYCenteringFromSuperview()
|
||||
dotView.frame.set(width: sideSize)
|
||||
dotView.frame.set(height: sideSize)
|
||||
dotView.setYCenter()
|
||||
dotView.frame.origin.x = currentXPosition
|
||||
dotView.round()
|
||||
currentXPosition += (sideSize + space)
|
||||
|
||||
@@ -21,12 +21,11 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPDownloadingButton: SPButton {
|
||||
public class SPDownloadingButton: SPButton {
|
||||
|
||||
let activityIndicatorView = UIActivityIndicatorView.init()
|
||||
var isFrameRounded: Bool = false
|
||||
|
||||
func startLoading() {
|
||||
public func startLoading() {
|
||||
self.activityIndicatorView.alpha = 0
|
||||
self.activityIndicatorView.isHidden = false
|
||||
self.activityIndicatorView.startAnimating()
|
||||
@@ -39,7 +38,10 @@ class SPDownloadingButton: SPButton {
|
||||
})
|
||||
}
|
||||
|
||||
func stopLoading() {
|
||||
public func stopLoading(newText: String? = nil) {
|
||||
if let newText = newText {
|
||||
self.setTitle(newText)
|
||||
}
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
self.activityIndicatorView.alpha = 0
|
||||
}, withComplection: {
|
||||
@@ -50,13 +52,9 @@ class SPDownloadingButton: SPButton {
|
||||
})
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
override public func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.activityIndicatorView.center = CGPoint.init(x: self.frame.width / 2, y: self.frame.height / 2)
|
||||
|
||||
if self.isFrameRounded {
|
||||
self.round()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPNativeLargeButton: SPDownloadingButton {
|
||||
public class SPNativeLargeButton: SPDownloadingButton {
|
||||
|
||||
override var isHighlighted: Bool {
|
||||
override public var isHighlighted: Bool {
|
||||
didSet {
|
||||
if self.gradientView == nil {
|
||||
if isHighlighted {
|
||||
@@ -42,29 +42,29 @@ class SPNativeLargeButton: SPDownloadingButton {
|
||||
}
|
||||
}
|
||||
|
||||
override func commonInit() {
|
||||
override public func commonInit() {
|
||||
super.commonInit()
|
||||
self.titleLabel?.font = UIFont.system(type: UIFont.BoldType.DemiBold, size: 16)
|
||||
self.titleLabel?.font = UIFont.system(weight: UIFont.FontWeight.demiBold, size: 16)
|
||||
self.setTitleColor(UIColor.white)
|
||||
self.backgroundColor = SPNativeStyleKit.Colors.blue
|
||||
self.backgroundColor = SPNativeColors.blue
|
||||
self.layer.masksToBounds = true
|
||||
self.layer.cornerRadius = 8
|
||||
self.contentEdgeInsets = UIEdgeInsets.init(top: 15, left: 15, bottom: 15, right: 15)
|
||||
}
|
||||
|
||||
override func sizeToFit() {
|
||||
override public 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)
|
||||
self.frame.set(width: width)
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
override public func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.gradientView?.setEqualsBoundsFromSuperview()
|
||||
self.gradientView?.setSuperviewBounds()
|
||||
self.gradientView?.layer.cornerRadius = self.layer.cornerRadius
|
||||
self.gradientView?.gradientLayer.cornerRadius = self.layer.cornerRadius
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPPlayCircleButton: UIButton {
|
||||
public class SPPlayCircleButton: UIButton {
|
||||
|
||||
var audioState: AudioState = AudioState.play {
|
||||
didSet {
|
||||
@@ -39,7 +39,7 @@ class SPPlayCircleButton: UIButton {
|
||||
}
|
||||
}
|
||||
|
||||
var iconColor = SPNativeStyleKit.Colors.white {
|
||||
var iconColor = SPNativeColors.white {
|
||||
didSet {
|
||||
self.iconView.color = self.iconColor
|
||||
}
|
||||
@@ -52,7 +52,7 @@ class SPPlayCircleButton: UIButton {
|
||||
self.addSubview(self.iconView)
|
||||
self.iconView.isUserInteractionEnabled = false
|
||||
self.setTitle("", for: .normal)
|
||||
self.backgroundColor = SPNativeStyleKit.Colors.blue
|
||||
self.backgroundColor = SPNativeColors.blue
|
||||
self.audioState = .play
|
||||
}
|
||||
|
||||
@@ -60,9 +60,9 @@ class SPPlayCircleButton: UIButton {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
override public func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.iconView.setEqualsFrameFromBounds(self, withWidthFactor: 0.45, withHeightFactor: 0.45, withCentering: true)
|
||||
self.iconView.setBounds(self, withWidthFactor: 0.45, withHeightFactor: 0.45, withCentering: true)
|
||||
self.round()
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPSocialButton: UIButton {
|
||||
public class SPSocialButton: UIButton {
|
||||
|
||||
let iconView = SPSocialIconView.init()
|
||||
var widthIconFactor: CGFloat = 0.5
|
||||
@@ -33,7 +33,7 @@ class SPSocialButton: UIButton {
|
||||
}
|
||||
}
|
||||
|
||||
override var isHighlighted: Bool {
|
||||
override public var isHighlighted: Bool {
|
||||
didSet {
|
||||
if isHighlighted {
|
||||
self.iconView.color = self.iconView.color.withAlphaComponent(0.7)
|
||||
@@ -43,7 +43,7 @@ class SPSocialButton: UIButton {
|
||||
}
|
||||
}
|
||||
|
||||
override var isEnabled: Bool {
|
||||
override public var isEnabled: Bool {
|
||||
didSet {
|
||||
if isEnabled {
|
||||
self.alpha = 1
|
||||
@@ -75,13 +75,13 @@ class SPSocialButton: UIButton {
|
||||
fileprivate func commonInit() {
|
||||
self.iconView.isUserInteractionEnabled = false
|
||||
self.addSubview(self.iconView)
|
||||
self.backgroundColor = SPNativeStyleKit.Colors.blue
|
||||
self.iconView.color = SPNativeStyleKit.Colors.white
|
||||
self.backgroundColor = SPNativeColors.blue
|
||||
self.iconView.color = SPNativeColors.white
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
override public func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.iconView.setEqualsFrameFromBounds(self, withWidthFactor: self.widthIconFactor, withHeightFactor: self.heightIconFactor, withCentering: true)
|
||||
self.iconView.setBounds(self, withWidthFactor: self.widthIconFactor, withHeightFactor: self.heightIconFactor, withCentering: true)
|
||||
self.round()
|
||||
}
|
||||
}
|
||||
|
||||
+18
-16
@@ -21,25 +21,26 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPSystemIconButton: UIButton {
|
||||
public class SPSystemIconButton: SPButton {
|
||||
|
||||
let iconView = SPSystemIconView.init()
|
||||
|
||||
var widthIconFactor: CGFloat = 1
|
||||
var heightIconFactor: CGFloat = 1
|
||||
|
||||
var type: SPSystemIconType {
|
||||
var icon: SPSystemIcon {
|
||||
didSet {
|
||||
self.iconView.type = self.type
|
||||
self.iconView.icon = self.icon
|
||||
}
|
||||
}
|
||||
|
||||
var color = SPNativeStyleKit.Colors.blue {
|
||||
var color = SPNativeColors.blue {
|
||||
didSet {
|
||||
self.iconView.color = self.color
|
||||
}
|
||||
}
|
||||
|
||||
override var isHighlighted: Bool {
|
||||
override public var isHighlighted: Bool {
|
||||
didSet {
|
||||
if isHighlighted {
|
||||
self.iconView.color = self.color.withAlphaComponent(0.7)
|
||||
@@ -49,17 +50,17 @@ class SPSystemIconButton: UIButton {
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
self.type = .share
|
||||
super.init(frame: CGRect.zero)
|
||||
override init() {
|
||||
self.icon = .share
|
||||
super.init()
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
init(type: SPSystemIconType) {
|
||||
self.type = type
|
||||
super.init(frame: CGRect.zero)
|
||||
self.iconView.type = self.type
|
||||
self.type = type
|
||||
init(type: SPSystemIcon) {
|
||||
self.icon = type
|
||||
super.init()
|
||||
self.iconView.icon = self.icon
|
||||
self.icon = type
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
@@ -67,13 +68,14 @@ class SPSystemIconButton: UIButton {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
fileprivate func commonInit() {
|
||||
override func commonInit() {
|
||||
super.commonInit()
|
||||
self.iconView.isUserInteractionEnabled = false
|
||||
self.addSubview(self.iconView)
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
override public func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.iconView.setEqualsFrameFromBounds(self, withWidthFactor: self.widthIconFactor, withHeightFactor: self.heightIconFactor, withCentering: true)
|
||||
self.iconView.setBounds(self, withWidthFactor: self.widthIconFactor, withHeightFactor: self.heightIconFactor, withCentering: true)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ class SPAudioIconView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
var color = SPNativeStyleKit.Colors.white {
|
||||
var color = SPNativeColors.white {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
+5
-1
@@ -29,7 +29,7 @@ class SPGolubevIconView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
var whiteColor = SPNativeStyleKit.Colors.white {
|
||||
var whiteColor = SPNativeColors.white {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
@@ -103,6 +103,9 @@ class SPGolubevIconView: UIView {
|
||||
case .headphones:
|
||||
SPCodeDraw.GolubevIconPack.drawHeadphones(frame: rect, resizing: .aspectFit, white: self.whiteColor, light: self.lightColor, medium: self.mediumColor, dark: self.darkColor)
|
||||
break
|
||||
case .windmill:
|
||||
SPCodeDraw.GolubevIconPack.drawWindmill(frame: rect, resizing: .aspectFit, white: self.whiteColor, light: self.lightColor, medium: self.mediumColor, dark: self.darkColor)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +119,7 @@ class SPGolubevIconView: UIView {
|
||||
case documents
|
||||
case compass
|
||||
case headphones
|
||||
case windmill
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ class SPSocialIconView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
var color = SPNativeStyleKit.Colors.blue {
|
||||
var color = SPNativeColors.blue {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
+6
-6
@@ -23,26 +23,26 @@ import UIKit
|
||||
|
||||
class SPSystemIconView: UIView {
|
||||
|
||||
var type: SPSystemIconType {
|
||||
var icon: SPSystemIcon {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
var color = SPNativeStyleKit.Colors.blue {
|
||||
var color = SPNativeColors.blue {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
self.type = .share
|
||||
self.icon = .share
|
||||
super.init(frame: CGRect.zero)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
init(type: SPSystemIconType) {
|
||||
self.type = type
|
||||
init(type: SPSystemIcon) {
|
||||
self.icon = type
|
||||
super.init(frame: CGRect.zero)
|
||||
self.commonInit()
|
||||
}
|
||||
@@ -57,7 +57,7 @@ class SPSystemIconView: UIView {
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
super.draw(rect)
|
||||
switch type {
|
||||
switch icon {
|
||||
case .share:
|
||||
SPCodeDraw.SystemIconPack.drawShare(frame: rect, resizing: .aspectFit, color: self.color)
|
||||
break
|
||||
+6
-6
@@ -21,7 +21,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPMengTransformCollectionViewCell: SPCollectionViewCell {
|
||||
public class SPMengTransformCollectionViewCell: SPCollectionViewCell {
|
||||
|
||||
let backgroundImageView = SPDownloadingImageView()
|
||||
let titleLabel = UILabel()
|
||||
@@ -86,8 +86,8 @@ class SPMengTransformCollectionViewCell: SPCollectionViewCell {
|
||||
self.backgroundImageView.bottomAnchor.constraint(equalTo:
|
||||
contentView.bottomAnchor, constant: 0).isActive = true
|
||||
|
||||
self.gradientView.setStartColorPosition(SPGradientView.Position.TopLeft)
|
||||
self.gradientView.setEndColorPosition(.BottomRight)
|
||||
self.gradientView.startColorPosition = .topLeft
|
||||
self.gradientView.endColorPosition = .bottomRight
|
||||
self.gradientView.isHidden = true
|
||||
self.gradientView.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.gradientView.layer.masksToBounds = false
|
||||
@@ -125,7 +125,7 @@ class SPMengTransformCollectionViewCell: SPCollectionViewCell {
|
||||
self.titleLabel.text = ""
|
||||
self.titleLabel.setDeepShadowForLetters()
|
||||
self.titleLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.titleLabel.font = UIFont.system(type: .DemiBold, size: 32)
|
||||
self.titleLabel.font = UIFont.system(weight: .demiBold, size: 32)
|
||||
self.titleLabel.textColor = UIColor.white
|
||||
self.titleLabel.numberOfLines = 0
|
||||
contentView.addSubview(self.titleLabel)
|
||||
@@ -140,7 +140,7 @@ class SPMengTransformCollectionViewCell: SPCollectionViewCell {
|
||||
self.subtitleLabel.text = ""
|
||||
self.subtitleLabel.setDeepShadowForLetters()
|
||||
self.subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.subtitleLabel.font = UIFont.system(type: .Regular, size: 17)
|
||||
self.subtitleLabel.font = UIFont.system(weight: .regular, size: 17)
|
||||
self.subtitleLabel.textColor = UIColor.white
|
||||
self.subtitleLabel.numberOfLines = 0
|
||||
contentView.addSubview(self.subtitleLabel)
|
||||
@@ -153,7 +153,7 @@ class SPMengTransformCollectionViewCell: SPCollectionViewCell {
|
||||
contentView.bottomAnchor, constant: -20).isActive = true
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
override public func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
self.backgroundImageView.image = nil
|
||||
self.titleLabel.text = ""
|
||||
+14
-6
@@ -196,7 +196,6 @@ public class SPCollectionViewLayout: UICollectionViewFlowLayout {
|
||||
|
||||
if isAllowInsertAnimation {
|
||||
if self.insertIndexPaths.contains(itemIndexPath) {
|
||||
//attributes?.center = CGPoint.init(x: attributes?.center.x ?? 0, y: 40)
|
||||
attributes?.alpha = 0
|
||||
attributes?.zIndex = 0
|
||||
attributes?.transform = CGAffineTransform.init(scaleX: self.minimumScaleFactor, y: self.minimumScaleFactor)
|
||||
@@ -207,11 +206,10 @@ public class SPCollectionViewLayout: UICollectionViewFlowLayout {
|
||||
}
|
||||
|
||||
override public func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
|
||||
var attributes: UICollectionViewLayoutAttributes?
|
||||
|
||||
let attributes: UICollectionViewLayoutAttributes? = super.finalLayoutAttributesForDisappearingItem(at: itemIndexPath)
|
||||
|
||||
if self.deleteIndexPaths.contains(itemIndexPath) {
|
||||
|
||||
attributes = self.layoutAttributesForItem(at: itemIndexPath)
|
||||
attributes?.alpha = 0
|
||||
attributes?.zIndex = 0
|
||||
attributes?.transform3D = CATransform3DScale(CATransform3DIdentity, self.minimumScaleFactor, self.minimumScaleFactor, 1)
|
||||
@@ -229,9 +227,19 @@ public class SPCollectionViewLayout: UICollectionViewFlowLayout {
|
||||
collectionView.decelerationRate = UIScrollView.DecelerationRate.fast
|
||||
|
||||
if cellSideRatio == nil {
|
||||
var height = collectionView.bounds.size.height * self.heightFactor
|
||||
if height > self.maxHeight {
|
||||
height = self.maxHeight
|
||||
}
|
||||
|
||||
var width = collectionView.bounds.size.width * self.widthFactor
|
||||
if width > self.maxWidth {
|
||||
width = self.maxWidth
|
||||
}
|
||||
|
||||
self.itemSize = CGSize.init(
|
||||
width: collectionView.bounds.size.width * self.widthFactor,
|
||||
height: collectionView.bounds.size.height * self.heightFactor
|
||||
width: width,
|
||||
height: height
|
||||
)
|
||||
} else {
|
||||
self.itemSize = SPLayout.sizeWith(
|
||||
+16
-10
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
public class SPCollectionView: UICollectionView {
|
||||
|
||||
var layout = SPCollectionViewLayout()
|
||||
let layout = UICollectionViewFlowLayout()
|
||||
private var cacheImages: [(link: String, image: UIImage)] = []
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
@@ -31,29 +31,35 @@ public class SPCollectionView: UICollectionView {
|
||||
commonInit()
|
||||
}
|
||||
|
||||
init(frame: CGRect) {
|
||||
super.init(frame: frame, collectionViewLayout: self.layout)
|
||||
commonInit()
|
||||
init() {
|
||||
super.init(frame: .zero, collectionViewLayout: self.layout)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
init() {
|
||||
super.init(frame: CGRect.zero, collectionViewLayout: self.layout)
|
||||
commonInit()
|
||||
public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
|
||||
super.init(frame: frame, collectionViewLayout: self.layout)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
internal func commonInit() {
|
||||
self.layout.scrollDirection = .vertical
|
||||
self.backgroundColor = UIColor.clear
|
||||
self.collectionViewLayout = self.layout
|
||||
self.decelerationRate = UIScrollView.DecelerationRate.fast
|
||||
self.delaysContentTouches = false
|
||||
self.isPagingEnabled = false
|
||||
self.showsHorizontalScrollIndicator = false
|
||||
self.showsVerticalScrollIndicator = false
|
||||
}
|
||||
|
||||
func height(rows: Int) -> CGFloat {
|
||||
return self.layout.itemSize.height * CGFloat(rows) + self.layout.minimumLineSpacing * CGFloat(rows - 1)
|
||||
}
|
||||
|
||||
func width(columns: Int) -> CGFloat {
|
||||
return self.layout.itemSize.width * CGFloat(columns) + self.layout.minimumInteritemSpacing * CGFloat(columns - 1)
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - cache
|
||||
//MARK: - Cache
|
||||
extension SPCollectionView {
|
||||
|
||||
func setCachedImage(link: String, indexPath: IndexPath, on imageView: SPDownloadingImageView, cell: SPCollectionViewCell) {
|
||||
+5
-5
@@ -21,7 +21,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
struct SPMengTransformCollectionData {
|
||||
public struct SPMengTransformCollectionData {
|
||||
|
||||
var title: String
|
||||
var subtitle: String
|
||||
@@ -40,7 +40,7 @@ struct SPMengTransformCollectionData {
|
||||
}
|
||||
}
|
||||
|
||||
class SPMengTransformCollectionView: SPCollectionView {
|
||||
public class SPMengTransformCollectionView: SPPageCollectionView {
|
||||
|
||||
var data: [SPMengTransformCollectionData] = []
|
||||
var withParalax: Bool = true
|
||||
@@ -73,11 +73,11 @@ class SPMengTransformCollectionView: SPCollectionView {
|
||||
|
||||
extension SPMengTransformCollectionView: UICollectionViewDataSource {
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return self.data.count
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
let data = self.data[indexPath.row]
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "sectionCell", for: indexPath) as! SPMengTransformCollectionViewCell
|
||||
cell.currentIndexPath = indexPath
|
||||
@@ -166,7 +166,7 @@ extension SPMengTransformCollectionView: UICollectionViewDataSource {
|
||||
|
||||
extension SPMengTransformCollectionView: UICollectionViewDelegate {
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
self.updateCells()
|
||||
}
|
||||
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
// 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 SPPageCollectionView: UICollectionView {
|
||||
|
||||
var layout = SPCollectionViewLayout()
|
||||
private var cacheImages: [(link: String, image: UIImage)] = []
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
init(frame: CGRect) {
|
||||
super.init(frame: frame, collectionViewLayout: self.layout)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
init() {
|
||||
super.init(frame: CGRect.zero, collectionViewLayout: self.layout)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
internal func commonInit() {
|
||||
self.layout.scrollDirection = .vertical
|
||||
self.backgroundColor = UIColor.clear
|
||||
self.collectionViewLayout = self.layout
|
||||
self.decelerationRate = UIScrollView.DecelerationRate.fast
|
||||
self.delaysContentTouches = false
|
||||
self.isPagingEnabled = false
|
||||
self.showsHorizontalScrollIndicator = false
|
||||
self.showsVerticalScrollIndicator = false
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - cache
|
||||
extension SPPageCollectionView {
|
||||
|
||||
func setCachedImage(link: String, indexPath: IndexPath, on imageView: SPDownloadingImageView, cell: SPCollectionViewCell) {
|
||||
if let image = self.fromCahce(link: link) {
|
||||
imageView.setImage(image: image, animatable: false)
|
||||
} else {
|
||||
SPDownloader.image(link: link) { (response) in
|
||||
if let image = response {
|
||||
if cell.currentIndexPath == indexPath {
|
||||
imageView.setImage(image: image, animatable: true)
|
||||
self.toCache(link: link, image: image)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
-162
@@ -1,162 +0,0 @@
|
||||
// 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
@@ -1,86 +0,0 @@
|
||||
// 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
@@ -1,202 +0,0 @@
|
||||
// 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
@@ -1,311 +0,0 @@
|
||||
// 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
@@ -1,245 +0,0 @@
|
||||
// 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
@@ -1,420 +0,0 @@
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 SPController: SPStatusBarManagerController {
|
||||
|
||||
let emptyTitlesView = SPEmptyLabelsView(title: "No Data", subtitle: "No data or information")
|
||||
|
||||
override public func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
self.emptyTitlesView.isHidden = true
|
||||
self.view.addSubview(self.emptyTitlesView)
|
||||
}
|
||||
|
||||
public override func viewWillLayoutSubviews() {
|
||||
super.viewWillLayoutSubviews()
|
||||
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)
|
||||
}
|
||||
|
||||
func updateLayout(with size: CGSize) {
|
||||
self.emptyTitlesView.layout(centerY: size.height / 2)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
+17
-32
@@ -21,7 +21,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPNativeTableViewController: SPBaseTableViewController {
|
||||
public class SPNativeTableController: SPTableController {
|
||||
|
||||
let labelTableViewCellIdentifier: String = "labelTableViewCellIdentifier"
|
||||
let textFieldTableViewCellIdentifier: String = "textFieldTableViewCellIdentifier"
|
||||
@@ -31,7 +31,6 @@ class SPNativeTableViewController: SPBaseTableViewController {
|
||||
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"
|
||||
@@ -40,17 +39,10 @@ class SPNativeTableViewController: SPBaseTableViewController {
|
||||
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() {
|
||||
override public func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.statusBar = .dark
|
||||
@@ -62,7 +54,7 @@ class SPNativeTableViewController: SPBaseTableViewController {
|
||||
self.tableView.contentInsetAdjustmentBehavior = .always
|
||||
}
|
||||
|
||||
self.tableView.backgroundColor = SPNativeStyleKit.Colors.customGray
|
||||
self.tableView.backgroundColor = SPNativeColors.customGray
|
||||
self.tableView.delaysContentTouches = false
|
||||
self.tableView.allowsSelection = false
|
||||
self.tableView.rowHeight = UITableView.automaticDimension
|
||||
@@ -78,35 +70,32 @@ class SPNativeTableViewController: SPBaseTableViewController {
|
||||
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.activityIndicatorView.color = SPNativeColors.gray
|
||||
self.view.addSubview(self.activityIndicatorView)
|
||||
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
}
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
override public func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return super.numberOfSections(in: tableView)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
override public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return super.tableView(tableView, numberOfRowsInSection: section)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
override public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
fatalError("SPNativeTableViewController - need ivveride cellForRowAt")
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
override public func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
return nil
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||
override public func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -150,10 +139,6 @@ class SPNativeTableViewController: SPBaseTableViewController {
|
||||
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
|
||||
@@ -170,9 +155,9 @@ class SPNativeTableViewController: SPBaseTableViewController {
|
||||
}
|
||||
|
||||
//MARK: - manage selection
|
||||
extension SPNativeTableViewController {
|
||||
extension SPNativeTableController {
|
||||
|
||||
override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
|
||||
override public func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
|
||||
|
||||
if let _ = tableView.cellForRow(at: indexPath) as? SPFormFeaturedTitleTableViewCell {
|
||||
return false
|
||||
@@ -184,9 +169,9 @@ extension SPNativeTableViewController {
|
||||
}
|
||||
|
||||
//MARK: - manage spaces
|
||||
extension SPNativeTableViewController {
|
||||
extension SPNativeTableController {
|
||||
|
||||
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
override public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
if section == 0 {
|
||||
return self.showTopInsets ? super.tableView(tableView, viewForHeaderInSection: section) : nil
|
||||
} else {
|
||||
@@ -194,7 +179,7 @@ extension SPNativeTableViewController {
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
|
||||
override public func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
|
||||
if section == self.tableView.lastSection {
|
||||
return self.showBottomInsets ? super.tableView(tableView, viewForFooterInSection: section) : nil
|
||||
} else {
|
||||
@@ -202,7 +187,7 @@ extension SPNativeTableViewController {
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
override public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
let firstSection = self.tableView.firstSectionWithRows
|
||||
if section == firstSection {
|
||||
if self.showTopInsets {
|
||||
@@ -230,7 +215,7 @@ extension SPNativeTableViewController {
|
||||
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
|
||||
override public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
|
||||
if section == self.tableView.lastSectionWithRows {
|
||||
if self.showBottomInsets {
|
||||
if self.autoBottomSpace {
|
||||
+69
-53
@@ -21,15 +21,20 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPProposeViewController: SPBaseViewController {
|
||||
public class SPProposeController: SPController {
|
||||
|
||||
private let data: Data
|
||||
internal let areaView = AreaView()
|
||||
private var isPresent: Bool = false
|
||||
|
||||
private var animationDuration: TimeInterval {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
private var gradeFactor: CGFloat {
|
||||
return 0.6
|
||||
}
|
||||
|
||||
private var space: CGFloat {
|
||||
return 6
|
||||
}
|
||||
@@ -53,7 +58,7 @@ class SPProposeViewController: SPBaseViewController {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
override public func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.areaView.isHidden = true
|
||||
@@ -74,36 +79,29 @@ class SPProposeViewController: SPBaseViewController {
|
||||
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
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
if !self.isPresent {
|
||||
self.present()
|
||||
self.isPresent = true
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
private func present() {
|
||||
SPVibration.impact(.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(self.gradeFactor)
|
||||
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) {
|
||||
|
||||
override public 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
|
||||
@@ -130,11 +128,18 @@ class SPProposeViewController: SPBaseViewController {
|
||||
}
|
||||
}
|
||||
|
||||
@objc func handleGesture(sender: UIPanGestureRecognizer) {
|
||||
override func updateLayout(with size: CGSize) {
|
||||
super.updateLayout(with: size)
|
||||
self.areaView.layout(origin: CGPoint.init(x: self.space, y: 0), width: size.width - (self.space * 2))
|
||||
self.areaView.frame.origin.y = self.view.frame.size.height - self.areaView.frame.height - (self.bottomSafeArea / 2) - self.space
|
||||
|
||||
}
|
||||
|
||||
@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
|
||||
self.view.backgroundColor = UIColor.black.withAlphaComponent(self.gradeFactor)
|
||||
}, spring: 1,
|
||||
velocity: 1,
|
||||
options: .transitionCurlDown,
|
||||
@@ -151,6 +156,13 @@ class SPProposeViewController: SPBaseViewController {
|
||||
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)
|
||||
|
||||
let baseY = self.view.frame.size.height - self.areaView.frame.height - (self.bottomSafeArea / 2) - self.space
|
||||
let dem = -(baseY - self.areaView.frame.origin.y) / 6
|
||||
var newGrade = self.gradeFactor - (dem / 100)
|
||||
newGrade.setIfFewer(when: 0.2)
|
||||
newGrade.setIfMore(when: self.gradeFactor + 0.05)
|
||||
self.view.backgroundColor = UIColor.black.withAlphaComponent(newGrade)
|
||||
case .ended:
|
||||
returnAreaViewToPoint()
|
||||
default:
|
||||
@@ -174,10 +186,9 @@ class SPProposeViewController: SPBaseViewController {
|
||||
let subtitleLabel = UILabel()
|
||||
let imageView = SPDownloadingImageView()
|
||||
let button = SPNativeLargeButton()
|
||||
let closeButton = SPSystemIconButton(type: SPSystemIconType.close)
|
||||
let closeButton = SPSystemIconButton(type: SPSystemIcon.close)
|
||||
|
||||
var imageSideSize: CGFloat = 160
|
||||
|
||||
private let space: CGFloat = 36
|
||||
|
||||
init() {
|
||||
@@ -186,18 +197,18 @@ class SPProposeViewController: SPBaseViewController {
|
||||
self.layer.masksToBounds = true
|
||||
self.layer.cornerRadius = 34
|
||||
|
||||
self.titleLabel.font = UIFont.system(type: .Regular, size: 28)
|
||||
self.titleLabel.font = UIFont.system(weight: .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.titleLabel.setCenterAlignment()
|
||||
self.addSubview(self.titleLabel)
|
||||
|
||||
self.subtitleLabel.font = UIFont.system(type: .Regular, size: 16)
|
||||
self.subtitleLabel.textColor = SPNativeStyleKit.Colors.black
|
||||
self.subtitleLabel.font = UIFont.system(weight: .regular, size: 16)
|
||||
self.subtitleLabel.textColor = SPNativeColors.black
|
||||
self.subtitleLabel.numberOfLines = 0
|
||||
self.subtitleLabel.setCenteringAlignment()
|
||||
self.subtitleLabel.setCenterAlignment()
|
||||
self.addSubview(self.subtitleLabel)
|
||||
|
||||
self.imageView.gradeView.backgroundColor = UIColor.white
|
||||
@@ -205,15 +216,16 @@ class SPProposeViewController: SPBaseViewController {
|
||||
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.button.titleLabel?.font = UIFont.system(weight: UIFont.FontWeight.medium, size: 15)
|
||||
self.button.setTitleColor(SPNativeColors.black)
|
||||
self.button.layer.cornerRadius = 13
|
||||
self.button.backgroundColor = SPNativeColors.lightGray
|
||||
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.closeButton.backgroundColor = SPNativeColors.lightGray.withAlphaComponent(0.6)
|
||||
self.closeButton.color = UIColor.init(hex: "979797")
|
||||
self.addSubview(self.closeButton)
|
||||
}
|
||||
|
||||
@@ -225,36 +237,40 @@ class SPProposeViewController: SPBaseViewController {
|
||||
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.titleLabel.frame.origin.y = self.space * 0.9
|
||||
self.titleLabel.frame.set(width: self.frame.width - self.space * 3)
|
||||
self.titleLabel.setXCenter()
|
||||
|
||||
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.subtitleLabel.frame.origin.y = self.titleLabel.frame.bottomY + 8
|
||||
self.subtitleLabel.frame.set(width: self.frame.width - self.space * 2)
|
||||
self.subtitleLabel.setXCenter()
|
||||
|
||||
self.imageView.frame = CGRect.init(
|
||||
x: 0, y: self.subtitleLabel.frame.bottomYPosition + self.space / 2,
|
||||
x: 0, y: self.subtitleLabel.frame.bottomY + self.space / 2,
|
||||
width: self.imageSideSize,
|
||||
height: self.imageSideSize
|
||||
)
|
||||
self.imageView.setXCenteringFromSuperview()
|
||||
self.imageView.setXCenter()
|
||||
|
||||
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.button.frame.set(height: 52)
|
||||
self.button.frame.set(width: self.frame.width - self.space * 2)
|
||||
self.button.frame.origin.y = self.imageView.frame.bottomY + self.space / 1.8
|
||||
self.button.setXCenter()
|
||||
|
||||
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()
|
||||
|
||||
self.frame.set(height: self.button.frame.bottomY + self.space)
|
||||
}
|
||||
|
||||
override func sizeToFit() {
|
||||
super.sizeToFit()
|
||||
self.setHeight(self.button.frame.bottomYPosition + self.space)
|
||||
func layout(origin: CGPoint, width: CGFloat) {
|
||||
self.frame.origin = origin
|
||||
self.frame.set(width: width)
|
||||
self.layoutSubviews()
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user