Compare commits
84 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| c36078cda2 | |||
| b78c82194f | |||
| a112473a04 | |||
| c1b135a30a | |||
| c1a7622a26 | |||
| c3421432b0 | |||
| 99353d3610 | |||
| 3bee6898e0 | |||
| 587790b1db | |||
| 2698cfe23d | |||
| 42b82886ca | |||
| 7af77b2596 | |||
| 7c663e7760 | |||
| f561e94110 | |||
| 3bf06ae2b3 | |||
| a6d5e6f97d | |||
| 0f20afb87c | |||
| 2368747150 | |||
| 32a010e1ca | |||
| fce0efaffa | |||
| 1500c272b1 | |||
| ba859e3646 | |||
| 01b8702c4a | |||
| ec809ee1c5 | |||
| a5f41ab3d6 | |||
| 58554f1449 | |||
| 8c1b6e2c8d | |||
| 724635e387 | |||
| dfc0fb8e52 | |||
| 50c9ab4104 | |||
| bb0821d2cc | |||
| ae29eba5b9 |
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
@@ -7,6 +7,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
|
||||
let navigationController = UINavigationController(rootViewController: Controller())
|
||||
self.launch(rootViewController: navigationController)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -32,6 +36,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
}
|
||||
|
||||
func launch(rootViewController: UIViewController) {
|
||||
let frame = UIScreen.main.bounds
|
||||
self.window = UIWindow(frame: frame)
|
||||
self.window!.rootViewController = rootViewController
|
||||
self.window!.makeKeyAndVisible()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,43 @@
|
||||
import UIKit
|
||||
|
||||
class Controller: UIViewController {
|
||||
|
||||
var presentControllerButton = UIButton.init(type: UIButton.ButtonType.system)
|
||||
var presentTableControllerButton = UIButton.init(type: UIButton.ButtonType.system)
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.view.backgroundColor = UIColor.white
|
||||
|
||||
self.presentControllerButton.setTitle("Show ViewController", for: .normal)
|
||||
self.presentControllerButton.addTarget(self, action: #selector(self.presentModalViewController), for: .touchUpInside)
|
||||
self.presentControllerButton.sizeToFit()
|
||||
self.presentControllerButton.center.x = self.view.frame.width / 2
|
||||
self.presentControllerButton.center.y = self.view.frame.height / 4 * 3
|
||||
self.view.addSubview(self.presentControllerButton)
|
||||
|
||||
self.presentTableControllerButton.setTitle("Show TableController", for: .normal)
|
||||
self.presentTableControllerButton.addTarget(self, action: #selector(self.presentModalTableViewController), for: .touchUpInside)
|
||||
self.presentTableControllerButton.sizeToFit()
|
||||
self.presentTableControllerButton.center.x = self.view.frame.width / 2
|
||||
self.presentTableControllerButton.frame.origin.y = self.presentControllerButton.frame.bottomYPosition + 10
|
||||
self.view.addSubview(self.presentTableControllerButton)
|
||||
}
|
||||
|
||||
@objc func presentModalViewController() {
|
||||
let modal = ModalViewController()
|
||||
let transitionDelegate = SPStorkTransitioningDelegate()
|
||||
modal.transitioningDelegate = transitionDelegate
|
||||
modal.modalPresentationStyle = .custom
|
||||
self.present(modal, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc func presentModalTableViewController() {
|
||||
let modal = ModalTableViewController()
|
||||
let transitionDelegate = SPStorkTransitioningDelegate()
|
||||
modal.transitioningDelegate = transitionDelegate
|
||||
modal.modalPresentationStyle = .custom
|
||||
self.present(modal, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,18 @@ public struct SPStorkController {
|
||||
}
|
||||
}
|
||||
|
||||
static public func updatePresentingController(parent controller: UIViewController) {
|
||||
if let presentationController = controller.presentedViewController?.presentationController as? SPStorkPresentationController {
|
||||
presentationController.updatePresentingController()
|
||||
}
|
||||
}
|
||||
|
||||
static public func updatePresentingController(modal controller: UIViewController) {
|
||||
if let presentationController = controller.presentationController as? SPStorkPresentationController {
|
||||
presentationController.updatePresentingController()
|
||||
}
|
||||
}
|
||||
|
||||
static private func controller(for view: UIView) -> UIViewController? {
|
||||
var nextResponder = view.next
|
||||
while nextResponder != nil && !(nextResponder! is UIViewController) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+41
-4
@@ -24,9 +24,14 @@ import UIKit
|
||||
class SPStorkPresentationController: UIPresentationController, UIGestureRecognizerDelegate {
|
||||
|
||||
var isSwipeToDismissEnabled: Bool = true
|
||||
var isTapAroundToDismissEnabled: 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 transitioningDelegate: SPStorkTransitioningDelegate?
|
||||
|
||||
var pan: UIPanGestureRecognizer?
|
||||
var tap: UITapGestureRecognizer?
|
||||
|
||||
private var indicatorView = SPStorkIndicatorView()
|
||||
private var gradeView: UIView = UIView()
|
||||
@@ -55,13 +60,20 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
|
||||
private var scaleForPresentingView: CGFloat {
|
||||
guard let containerView = containerView else { return 0 }
|
||||
let factor = 1 - (topSpace * 2 / containerView.frame.height)
|
||||
let factor = 1 - (self.topSpace * 2 / containerView.frame.height)
|
||||
return factor
|
||||
}
|
||||
|
||||
override var frameOfPresentedViewInContainerView: CGRect {
|
||||
guard let containerView = containerView else { return .zero }
|
||||
let yOffset: CGFloat = topSpace + 13
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -71,6 +83,7 @@ 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
|
||||
presentedView.addSubview(self.indicatorView)
|
||||
}
|
||||
self.updateLayoutIndicator()
|
||||
@@ -82,6 +95,7 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
containerView.insertSubview(self.snapshotViewContainer, belowSubview: presentedViewController.view)
|
||||
self.snapshotViewContainer.frame = initialFrame
|
||||
self.updateSnapshot()
|
||||
self.snapshotView?.layer.cornerRadius = 0
|
||||
self.backgroundView.backgroundColor = UIColor.black
|
||||
self.backgroundView.translatesAutoresizingMaskIntoConstraints = false
|
||||
containerView.insertSubview(self.backgroundView, belowSubview: self.snapshotViewContainer)
|
||||
@@ -145,14 +159,21 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
override func presentationTransitionDidEnd(_ completed: Bool) {
|
||||
super.presentationTransitionDidEnd(completed)
|
||||
guard let containerView = containerView else { return }
|
||||
self.updateSnapshot()
|
||||
self.presentedViewController.view.frame = self.frameOfPresentedViewInContainerView
|
||||
self.snapshotViewContainer.transform = .identity
|
||||
self.snapshotViewContainer.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.snapshotViewContainer.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
|
||||
self.updateSnapshotAspectRatio()
|
||||
|
||||
if self.isTapAroundToDismissEnabled {
|
||||
self.tap = UITapGestureRecognizer.init(target: self, action: #selector(self.handleTap))
|
||||
self.tap?.cancelsTouchesInView = false
|
||||
self.snapshotViewContainer.addGestureRecognizer(self.tap!)
|
||||
}
|
||||
|
||||
if self.isSwipeToDismissEnabled {
|
||||
self.pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
|
||||
self.pan = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan))
|
||||
self.pan!.delegate = self
|
||||
self.pan!.maximumNumberOfTouches = 1
|
||||
self.pan!.cancelsTouchesInView = false
|
||||
@@ -160,6 +181,10 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
}
|
||||
}
|
||||
|
||||
@objc func handleTap() {
|
||||
self.presentedViewController.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
override func dismissalTransitionWillBegin() {
|
||||
super.dismissalTransitionWillBegin()
|
||||
guard let containerView = containerView else { return }
|
||||
@@ -245,6 +270,8 @@ extension SPStorkPresentationController {
|
||||
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
|
||||
@@ -284,12 +311,17 @@ extension SPStorkPresentationController {
|
||||
}
|
||||
}
|
||||
|
||||
func updatePresentingController() {
|
||||
if self.startDismissing { return }
|
||||
self.updateSnapshot()
|
||||
}
|
||||
|
||||
private func updatePresentedViewForTranslation(inVerticalDirection translation: CGFloat) {
|
||||
if self.startDismissing { return }
|
||||
|
||||
let elasticThreshold: CGFloat = 120
|
||||
let translationFactor: CGFloat = 1 / 2
|
||||
|
||||
|
||||
if translation >= 0 {
|
||||
let translationForModal: CGFloat = {
|
||||
if translation >= elasticThreshold {
|
||||
@@ -348,6 +380,11 @@ extension SPStorkPresentationController {
|
||||
self.snapshotViewContainer.addSubview(currentSnapshotView)
|
||||
self.constraints(view: currentSnapshotView, to: self.snapshotViewContainer)
|
||||
self.snapshotView = currentSnapshotView
|
||||
self.snapshotView?.layer.cornerRadius = self.cornerRadius
|
||||
self.snapshotView?.layer.masksToBounds = true
|
||||
if #available(iOS 11.0, *) {
|
||||
snapshotView?.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
}
|
||||
self.gradeView.removeFromSuperview()
|
||||
self.gradeView.backgroundColor = UIColor.black
|
||||
self.snapshotView!.addSubview(self.gradeView)
|
||||
|
||||
+8
@@ -24,10 +24,18 @@ import UIKit
|
||||
public final class SPStorkTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
|
||||
|
||||
public var isSwipeToDismissEnabled: Bool = true
|
||||
public var isTapAroundToDismissEnabled: Bool = true
|
||||
public var showIndicator: Bool = true
|
||||
public var indicatorColor: UIColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
|
||||
public var customHeight: CGFloat? = nil
|
||||
|
||||
public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
|
||||
let controller = SPStorkPresentationController(presentedViewController: presented, presenting: presenting)
|
||||
controller.isSwipeToDismissEnabled = self.isSwipeToDismissEnabled
|
||||
controller.isTapAroundToDismissEnabled = self.isTapAroundToDismissEnabled
|
||||
controller.showIndicator = self.showIndicator
|
||||
controller.indicatorColor = self.indicatorColor
|
||||
controller.customHeight = self.customHeight
|
||||
controller.transitioningDelegate = self
|
||||
return controller
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
import StoreKit
|
||||
|
||||
struct SPApp {
|
||||
|
||||
@@ -56,5 +57,12 @@ struct SPApp {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
static var number: Int {
|
||||
get {
|
||||
return UIApplication.shared.applicationIconBadgeNumber
|
||||
}
|
||||
set {
|
||||
UIApplication.shared.applicationIconBadgeNumber = newValue
|
||||
}
|
||||
}
|
||||
set {
|
||||
UserDefaults.standard.set(newValue, forKey: "SPLaunchCount")
|
||||
|
||||
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()
|
||||
struct Launch {
|
||||
|
||||
static func run() {
|
||||
self.count += 1
|
||||
}
|
||||
|
||||
static var count: Int {
|
||||
get {
|
||||
return UserDefaults.standard.value(forKey: "SPLaunchCount") as? Int ?? 0
|
||||
}
|
||||
set {
|
||||
UserDefaults.standard.set(newValue, forKey: "SPLaunchCount")
|
||||
}
|
||||
}
|
||||
|
||||
static var 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 {
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)})
|
||||
}
|
||||
@@ -56,6 +56,12 @@ struct SPAppStore {
|
||||
}
|
||||
}
|
||||
|
||||
static func requestReview() {
|
||||
if #available(iOS 10.3, *) {
|
||||
SKStoreReviewController.requestReview()
|
||||
}
|
||||
}
|
||||
|
||||
static func isUpdateAvailable(completion: @escaping (Bool)->()) {
|
||||
|
||||
guard let info = Bundle.main.infoDictionary,
|
||||
@@ -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)})
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ public struct SPAudio {
|
||||
private init() {}
|
||||
}
|
||||
|
||||
// Helper function inserted by Swift 4.2 migrator.
|
||||
fileprivate func convertFromAVAudioSessionCategory(_ input: AVAudioSession.Category) -> String {
|
||||
return input.rawValue
|
||||
}
|
||||
|
||||
@@ -21,4 +21,4 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
struct SPCodeDraw { private init(){} }
|
||||
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)
|
||||
])
|
||||
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() {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -23,11 +23,11 @@ import UIKit
|
||||
|
||||
struct SPDevice {
|
||||
|
||||
static var isIphone: Bool {
|
||||
static var iphone: Bool {
|
||||
return UIDevice.current.userInterfaceIdiom == .phone
|
||||
}
|
||||
|
||||
static var isIpad: Bool {
|
||||
static var ipad: Bool {
|
||||
return UIDevice.current.userInterfaceIdiom == .pad
|
||||
}
|
||||
|
||||
|
||||
+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
|
||||
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
|
||||
}
|
||||
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,6 +24,10 @@ import UIKit
|
||||
|
||||
extension String {
|
||||
|
||||
var digits: String {
|
||||
return components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
|
||||
}
|
||||
|
||||
mutating func dropLast(substring: String) {
|
||||
if self.hasSuffix(substring) {
|
||||
self = String(dropLast(substring.count))
|
||||
@@ -69,4 +73,8 @@ extension String {
|
||||
mutating func replace(_ replacingString: String, with newString: String) {
|
||||
self = self.replacingOccurrences(of: replacingString, with: newString)
|
||||
}
|
||||
|
||||
func replace(_ replacingString: String, with newString: String) -> String {
|
||||
return self.replacingOccurrences(of: replacingString, with: newString)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,11 +98,11 @@ extension UIButton {
|
||||
}
|
||||
|
||||
func setAnimatableText(_ text: String, withComplection completion: (() -> Void)! = {}) {
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
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()
|
||||
|
||||
@@ -23,55 +23,10 @@ import UIKit
|
||||
|
||||
public extension UIFont {
|
||||
|
||||
public struct fonts {
|
||||
|
||||
public static func AvenirNext(type: BoldType, size: CGFloat) -> UIFont {
|
||||
return UIFont.createFont(.AvenirNext, boldType: type, size: size)
|
||||
}
|
||||
}
|
||||
|
||||
public static func system(type: BoldType, size: CGFloat) -> UIFont {
|
||||
if #available(iOS 8.2, *) {
|
||||
return UIFont.systemFont(ofSize: size, weight: self.getBoldTypeBy(boldType: type))
|
||||
} else {
|
||||
return self.createFont(.AvenirNext, boldType: type, size: size)
|
||||
}
|
||||
return UIFont.systemFont(ofSize: size, weight: self.getBoldTypeBy(boldType: type))
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -94,10 +49,6 @@ public extension UIFont {
|
||||
}
|
||||
}
|
||||
|
||||
public enum FontType {
|
||||
case AvenirNext
|
||||
}
|
||||
|
||||
public enum BoldType {
|
||||
case Regular
|
||||
case Medium
|
||||
|
||||
+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
|
||||
}
|
||||
|
||||
|
||||
@@ -53,4 +53,25 @@ public extension UILabel {
|
||||
attributedText = NSAttributedString(string: textString, attributes: attrs)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ extension UINavigationController {
|
||||
if UINavigationBar.appearance().tintColor != nil {
|
||||
return UINavigationBar.appearance().tintColor
|
||||
} else {
|
||||
return SPNativeStyleKit.Colors.blue
|
||||
return SPNativeColors.blue
|
||||
}
|
||||
}
|
||||
set {
|
||||
|
||||
+13
@@ -23,6 +23,19 @@ import UIKit
|
||||
|
||||
extension UITabBarController {
|
||||
|
||||
static var elementsColor: UIColor {
|
||||
get {
|
||||
if UITabBar.appearance().tintColor != nil {
|
||||
return UITabBar.appearance().tintColor
|
||||
} else {
|
||||
return SPNativeColors.blue
|
||||
}
|
||||
}
|
||||
set {
|
||||
UINavigationBar.appearance().tintColor = newValue
|
||||
}
|
||||
}
|
||||
|
||||
func addTabBarItem(title: String, image: UIImage, selectedImage: UIImage? = nil, controller: UIViewController) {
|
||||
|
||||
let tabBarItem = UITabBarItem(
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ extension UITableViewCell {
|
||||
}
|
||||
set {
|
||||
let backgroundView = UIView()
|
||||
backgroundView.backgroundColor = SPNativeStyleKit.Colors.customGray
|
||||
backgroundView.backgroundColor = SPNativeColors.customGray
|
||||
self.selectedBackgroundView = backgroundView
|
||||
}
|
||||
}
|
||||
|
||||
+12
-12
@@ -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!)
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -34,20 +34,17 @@ public extension UIView {
|
||||
|
||||
public extension UIView {
|
||||
|
||||
var topSafeArea: CGFloat {
|
||||
var topSafeArea: CGFloat = 0
|
||||
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
|
||||
func set(width: CGFloat, height: CGFloat) {
|
||||
self.setHeight(height)
|
||||
self.setWidth(width)
|
||||
}
|
||||
|
||||
func setHeight(_ height: CGFloat) {
|
||||
@@ -82,10 +79,10 @@ public extension UIView {
|
||||
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!))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +150,7 @@ public extension 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
|
||||
|
||||
@@ -23,20 +23,30 @@ import UIKit
|
||||
|
||||
struct SPLayout {
|
||||
|
||||
static func sizeWith(widthFactor: CGFloat, maxWidth: CGFloat, heightFactor: CGFloat, maxHeight: CGFloat, relativeSideFactor: CGFloat, from relativeSize: CGSize) -> CGSize {
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ struct SPMail {
|
||||
return MFMailComposeViewController.canSendMail()
|
||||
}
|
||||
|
||||
static func openMailApp(to email: String, subject: String? = nil, body: String? = nil) {
|
||||
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) {
|
||||
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
|
||||
|
||||
enum SPNativeColors {
|
||||
|
||||
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")
|
||||
}
|
||||
+2
-2
@@ -29,7 +29,7 @@ struct SPLocalNotification {
|
||||
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)
|
||||
@@ -55,7 +55,7 @@ struct SPLocalNotification {
|
||||
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)
|
||||
|
||||
@@ -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)})
|
||||
}
|
||||
@@ -74,7 +74,7 @@ public extension CGFloat {
|
||||
}
|
||||
|
||||
public extension Collection {
|
||||
/// Return a copy of `self` with its elements shuffled
|
||||
|
||||
func shuffle() -> [Iterator.Element] {
|
||||
var list = Array(self)
|
||||
list.shuffleInPlace()
|
||||
@@ -90,9 +90,9 @@ extension Collection where Index == Int {
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -24,11 +24,7 @@ import UIKit
|
||||
class SPInstagram {
|
||||
|
||||
static var isSetApp: Bool {
|
||||
if UIApplication.shared.canOpenURL(URL(string: "instagram://user?username=test")!) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return UIApplication.shared.canOpenURL(URL(string: "instagram://user?username=test")!)
|
||||
}
|
||||
|
||||
static func openPost(id: String) {
|
||||
@@ -38,7 +34,7 @@ class SPInstagram {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,14 +45,13 @@ class SPInstagram {
|
||||
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)})
|
||||
}
|
||||
|
||||
@@ -24,11 +24,7 @@ import UIKit
|
||||
class SPTelegram {
|
||||
|
||||
static var isSetApp: Bool {
|
||||
if UIApplication.shared.canOpenURL(URL(string: "tg://msg?text=test")!) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return UIApplication.shared.canOpenURL(URL(string: "tg://msg?text=test")!)
|
||||
}
|
||||
|
||||
static func share(text: String, complection: @escaping (_ isOpened: Bool)->() = {_ in }) {
|
||||
@@ -47,7 +43,7 @@ class SPTelegram {
|
||||
|
||||
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) {
|
||||
@@ -56,13 +52,12 @@ class SPTelegram {
|
||||
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)})
|
||||
}
|
||||
|
||||
@@ -24,11 +24,7 @@ import UIKit
|
||||
class SPTwitter {
|
||||
|
||||
static var isSetApp: Bool {
|
||||
if UIApplication.shared.canOpenURL(URL(string: "twitter://post?message=test")!) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return UIApplication.shared.canOpenURL(URL(string: "twitter://post?message=test")!)
|
||||
}
|
||||
|
||||
static func share(text: String, complection: @escaping (_ isOpened: Bool)->() = {_ in }) {
|
||||
@@ -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)})
|
||||
}
|
||||
|
||||
@@ -24,11 +24,7 @@ import UIKit
|
||||
class SPViber {
|
||||
|
||||
static var isSetApp: Bool {
|
||||
if UIApplication.shared.canOpenURL(URL(string: "viber://forward?text=test")!) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return UIApplication.shared.canOpenURL(URL(string: "viber://forward?text=test")!)
|
||||
}
|
||||
|
||||
static func share(text: String, complection: @escaping (_ isOpened: Bool)->() = {_ in }) {
|
||||
@@ -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)})
|
||||
}
|
||||
|
||||
@@ -24,11 +24,7 @@ import UIKit
|
||||
class SPWhatsApp {
|
||||
|
||||
static var isSetApp: Bool {
|
||||
if UIApplication.shared.canOpenURL(URL(string: "whatsapp://send?text=test")!) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return UIApplication.shared.canOpenURL(URL(string: "whatsapp://send?text=test")!)
|
||||
}
|
||||
|
||||
static func share(text: String, complection: @escaping (_ isOpened: Bool)->() = {_ in }) {
|
||||
@@ -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,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,11 +26,6 @@ public enum SPStatusBar {
|
||||
case light
|
||||
}
|
||||
|
||||
public enum SPSnapToSide {
|
||||
case left
|
||||
case right
|
||||
}
|
||||
|
||||
public enum SPSystemIconType {
|
||||
case share
|
||||
case close
|
||||
@@ -73,3 +68,8 @@ public enum SPSystemApp {
|
||||
case photos
|
||||
case setting
|
||||
}
|
||||
|
||||
public enum SPSelectionType {
|
||||
case select
|
||||
case unselect
|
||||
}
|
||||
|
||||
@@ -23,21 +23,21 @@ import UIKit
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,11 +46,11 @@ class SPAppleMusicButton: SPButton {
|
||||
self.layer.cornerRadius = 8
|
||||
self.titleLabel?.font = UIFont.system(type: .DemiBold, size: 15)
|
||||
self.contentEdgeInsets = UIEdgeInsets.init(top: 12, left: 27, bottom: 12, right: 27)
|
||||
self.mode = .unselect
|
||||
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
|
||||
|
||||
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.set(width: width, height: sectionHeight)
|
||||
self.layoutSubviews()
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.topSeparatorView.frame.origin = .zero
|
||||
self.topSeparatorView.setWidth(self.frame.width)
|
||||
|
||||
self.bottomSeparatorView.frame.origin.x = 0
|
||||
self.bottomSeparatorView.frame.bottomYPosition = self.frame.height
|
||||
self.bottomSeparatorView.setWidth(self.frame.width)
|
||||
|
||||
let buttonWidth = (self.frame.width - self.buttonsSpace) / 2
|
||||
|
||||
self.leftButton.sizeToFit()
|
||||
self.leftButton.setWidth(buttonWidth)
|
||||
self.leftButton.frame.origin.x = 0
|
||||
self.leftButton.center.y = self.frame.height / 2
|
||||
|
||||
self.rightButton.sizeToFit()
|
||||
self.rightButton.setWidth(buttonWidth)
|
||||
self.rightButton.frame.bottomXPosition = self.frame.width
|
||||
self.rightButton.center.y = self.frame.height / 2
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -46,7 +46,7 @@ class SPNativeLargeButton: SPDownloadingButton {
|
||||
super.commonInit()
|
||||
self.titleLabel?.font = UIFont.system(type: UIFont.BoldType.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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -75,8 +75,8 @@ 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() {
|
||||
|
||||
@@ -33,7 +33,7 @@ class SPSystemIconButton: UIButton {
|
||||
}
|
||||
}
|
||||
|
||||
var color = SPNativeStyleKit.Colors.blue {
|
||||
var color = SPNativeColors.blue {
|
||||
didSet {
|
||||
self.iconView.color = self.color
|
||||
}
|
||||
|
||||
-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,64 @@
|
||||
// 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 = SPEmptyTitlesView(title: "No Data", subtitle: "No data or information")
|
||||
|
||||
override public func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
self.emptyTitlesView.isHidden = true
|
||||
self.view.addSubview(self.emptyTitlesView)
|
||||
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) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
+6
-19
@@ -21,7 +21,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPNativeTableViewController: SPBaseTableViewController {
|
||||
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,16 +39,9 @@ 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() {
|
||||
super.viewDidLoad()
|
||||
|
||||
@@ -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,13 +70,12 @@ 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)
|
||||
@@ -150,10 +141,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,7 +157,7 @@ class SPNativeTableViewController: SPBaseTableViewController {
|
||||
}
|
||||
|
||||
//MARK: - manage selection
|
||||
extension SPNativeTableViewController {
|
||||
extension SPNativeTableController {
|
||||
|
||||
override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
|
||||
|
||||
@@ -184,7 +171,7 @@ extension SPNativeTableViewController {
|
||||
}
|
||||
|
||||
//MARK: - manage spaces
|
||||
extension SPNativeTableViewController {
|
||||
extension SPNativeTableController {
|
||||
|
||||
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
if section == 0 {
|
||||
+29
-24
@@ -21,10 +21,11 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPProposeViewController: SPBaseViewController {
|
||||
class SPProposeController: SPController {
|
||||
|
||||
private let data: Data
|
||||
internal let areaView = AreaView()
|
||||
private var isPresent: Bool = false
|
||||
|
||||
private var animationDuration: TimeInterval {
|
||||
return 0.5
|
||||
@@ -78,28 +79,24 @@ class SPProposeViewController: SPBaseViewController {
|
||||
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 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(system: .warning)
|
||||
self.areaView.frame.origin.y = self.view.frame.size.height
|
||||
self.areaView.isHidden = false
|
||||
SPAnimationSpring.animate(self.animationDuration, animations: {
|
||||
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.6)
|
||||
self.areaView.frame.origin.y = self.view.frame.size.height - self.areaView.frame.height - (self.bottomSafeArea / 2) - self.space
|
||||
}, spring: 1,
|
||||
velocity: 1,
|
||||
options: .transitionCurlUp)
|
||||
}
|
||||
|
||||
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
@@ -130,8 +127,16 @@ class SPProposeViewController: SPBaseViewController {
|
||||
}
|
||||
}
|
||||
|
||||
@objc func handleGesture(sender: UIPanGestureRecognizer) {
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
@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
|
||||
@@ -195,7 +200,7 @@ class SPProposeViewController: SPBaseViewController {
|
||||
self.addSubview(self.titleLabel)
|
||||
|
||||
self.subtitleLabel.font = UIFont.system(type: .Regular, size: 16)
|
||||
self.subtitleLabel.textColor = SPNativeStyleKit.Colors.black
|
||||
self.subtitleLabel.textColor = SPNativeColors.black
|
||||
self.subtitleLabel.numberOfLines = 0
|
||||
self.subtitleLabel.setCenteringAlignment()
|
||||
self.addSubview(self.subtitleLabel)
|
||||
@@ -206,7 +211,7 @@ class SPProposeViewController: SPBaseViewController {
|
||||
self.addSubview(self.imageView)
|
||||
|
||||
self.button.titleLabel?.font = UIFont.system(type: UIFont.BoldType.Medium, size: 15)
|
||||
self.button.setTitleColor(SPNativeStyleKit.Colors.black)
|
||||
self.button.setTitleColor(SPNativeColors.black)
|
||||
self.button.backgroundColor = UIColor.init(hex: "D4D3DB")
|
||||
self.addSubview(self.button)
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPStatusBarManagerViewController: UIViewController {
|
||||
public class SPStatusBarManagerController: UIViewController {
|
||||
|
||||
var statusBar: SPStatusBar = .dark {
|
||||
didSet {
|
||||
@@ -57,7 +57,7 @@ public class SPStatusBarManagerViewController: UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
public class SPStatusBarManagerTableViewController: UITableViewController {
|
||||
public class SPStatusBarManagerTableController: UITableViewController {
|
||||
|
||||
var statusBar: SPStatusBar = .dark {
|
||||
didSet {
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
// 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 SPTableController: SPStatusBarManagerTableController {
|
||||
|
||||
var activityIndicatorView = UIActivityIndicatorView.init()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.activityIndicatorView.stopAnimating()
|
||||
self.activityIndicatorView.color = SPNativeColors.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) {
|
||||
self.activityIndicatorView.center = CGPoint.init(
|
||||
x: size.width / 2,
|
||||
y: (size.height - self.safeArea.top - self.safeArea.bottom) / 2
|
||||
)
|
||||
}
|
||||
|
||||
//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()
|
||||
}
|
||||
}
|
||||
|
||||
//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:))
|
||||
)
|
||||
}
|
||||
}
|
||||
-218
@@ -1,218 +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 SPWelcomeViewController: SPBaseViewController {
|
||||
|
||||
let imageView = SPDownloadingImageView()
|
||||
let titleLabel = UILabel()
|
||||
let subtitleLabel = UILabel()
|
||||
let descriptionLabel = UILabel()
|
||||
let commentLabel = UILabel()
|
||||
let button = SPNativeLargeButton()
|
||||
|
||||
private var data: SPWelcomeData
|
||||
private var views: [UIView] = []
|
||||
private var isPresented: Bool = false
|
||||
|
||||
static var isNeedPresent: Bool {
|
||||
return self.isFirstLaunch || self.hasBeenUpdated
|
||||
}
|
||||
|
||||
init(data: SPWelcomeData) {
|
||||
self.data = data
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
self.data = SPWelcomeData()
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.statusBar = self.data.statusBarStyle
|
||||
self.view.backgroundColor = self.data.backgroundColor
|
||||
|
||||
self.imageView.setImage(image: self.data.image, animatable: false)
|
||||
self.imageView.contentMode = .scaleAspectFit
|
||||
self.imageView.layer.cornerRadius = 6
|
||||
self.imageView.layer.masksToBounds = true
|
||||
self.view.addSubview(self.imageView)
|
||||
self.views.append(self.imageView)
|
||||
|
||||
self.titleLabel.text = self.data.title
|
||||
self.titleLabel.font = UIFont.system(type: UIFont.BoldType.Heavy, size: 50)
|
||||
self.titleLabel.numberOfLines = 1
|
||||
self.titleLabel.adjustsFontSizeToFitWidth = true
|
||||
self.titleLabel.minimumScaleFactor = 0.5
|
||||
self.titleLabel.textColor = self.data.textColor
|
||||
self.titleLabel.setLettersSpacing(-2)
|
||||
self.view.addSubview(self.titleLabel)
|
||||
self.views.append(self.titleLabel)
|
||||
|
||||
self.subtitleLabel.text = self.data.subtitle
|
||||
self.subtitleLabel.font = UIFont.system(type: UIFont.BoldType.Heavy, size: 50)
|
||||
self.subtitleLabel.textColor = self.data.color
|
||||
self.subtitleLabel.numberOfLines = 1
|
||||
self.subtitleLabel.adjustsFontSizeToFitWidth = true
|
||||
self.subtitleLabel.minimumScaleFactor = 0.5
|
||||
self.subtitleLabel.setLettersSpacing(-2)
|
||||
self.view.addSubview(self.subtitleLabel)
|
||||
self.views.append(self.subtitleLabel)
|
||||
|
||||
self.descriptionLabel.text = self.data.description
|
||||
if SPWelcomeViewController.hasBeenUpdated {
|
||||
self.descriptionLabel.text = self.data.changes
|
||||
}
|
||||
|
||||
self.descriptionLabel.font = UIFont.system(type: UIFont.BoldType.Regular, size: 17)
|
||||
self.descriptionLabel.textColor = self.data.textColor
|
||||
self.descriptionLabel.numberOfLines = 0
|
||||
self.descriptionLabel.setLettersSpacing(-0.2)
|
||||
self.view.addSubview(self.descriptionLabel)
|
||||
self.views.append(self.descriptionLabel)
|
||||
|
||||
self.commentLabel.text = self.data.comment
|
||||
self.commentLabel.font = UIFont.system(type: UIFont.BoldType.Regular, size: 13)
|
||||
self.commentLabel.textColor = UIColor.gray
|
||||
self.commentLabel.numberOfLines = 0
|
||||
self.commentLabel.setCenteringAlignment()
|
||||
self.view.addSubview(self.commentLabel)
|
||||
self.views.append(self.commentLabel)
|
||||
|
||||
self.button.setTitle(self.data.buttonTitle, for: UIControl.State.normal)
|
||||
self.button.backgroundColor = self.data.color
|
||||
self.button.addTarget(self, action: #selector(self.tapButton(sender:)), for: UIControl.Event.touchUpInside)
|
||||
self.view.addSubview(self.button)
|
||||
self.views.append(self.button)
|
||||
|
||||
for view in self.views {
|
||||
view.alpha = 0
|
||||
}
|
||||
|
||||
self.updateLayout(with: self.view.frame.size)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
if !self.isPresented {
|
||||
SPAnimationAlpha.showList(0.5, views: self.views, delayPerItem: 0.09)
|
||||
self.isPresented = true
|
||||
SPWelcomeViewController.updateCurrentVersion()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func tapButton(sender: UIButton) {
|
||||
SPVibration.impact(SPVibration.Style.light)
|
||||
self.data.complection(self.button)
|
||||
}
|
||||
|
||||
override func updateLayout(with size: CGSize) {
|
||||
super.updateLayout(with: size)
|
||||
|
||||
let sideSpace: CGFloat = size.width * 0.112
|
||||
|
||||
self.imageView.frame = CGRect.init(x: sideSpace, y: self.safeArea.top + (size.height * 0.1), width: 84, height: 84)
|
||||
|
||||
let space: CGFloat = size.height * 0.025
|
||||
|
||||
self.titleLabel.sizeToFit()
|
||||
self.titleLabel.frame = CGRect.init(x: sideSpace, y: self.imageView.frame.bottomYPosition + space, width: self.view.frame.width - sideSpace * 2, height: self.titleLabel.frame.height)
|
||||
|
||||
self.subtitleLabel.sizeToFit()
|
||||
self.subtitleLabel.frame = CGRect.init(x: sideSpace, y: self.titleLabel.frame.bottomYPosition - 12, width: self.view.frame.width - sideSpace * 2, height: self.subtitleLabel.frame.height)
|
||||
|
||||
self.descriptionLabel.frame = CGRect.init(x: sideSpace, y: self.subtitleLabel.frame.bottomYPosition + space, width: self.view.frame.width - sideSpace * 2, height: self.descriptionLabel.frame.height)
|
||||
self.descriptionLabel.sizeToFit()
|
||||
|
||||
self.button.sizeToFit()
|
||||
self.button.frame.origin.y = size.height - self.safeArea.bottom - space - self.button.frame.height
|
||||
self.button.setWidth(size.width - sideSpace * 2)
|
||||
self.button.center.x = size.width / 2
|
||||
|
||||
let sideSpaceForCommentLabel: CGFloat = sideSpace * 1.5
|
||||
self.commentLabel.frame = CGRect.init(x: sideSpaceForCommentLabel, y: 0, width: self.view.frame.width - sideSpaceForCommentLabel * 2, height: 0)
|
||||
self.commentLabel.sizeToFit()
|
||||
self.commentLabel.center.x = size.width / 2
|
||||
self.commentLabel.frame.origin.y = self.button.frame.origin.y - space - self.commentLabel.frame.height
|
||||
}
|
||||
}
|
||||
|
||||
extension SPWelcomeViewController {
|
||||
|
||||
static private let versionKey = "SPWelcomeControllerCurrentVersionKey"
|
||||
static private let firstLaucnhKey = "SPWelcomeControllerFirstLaucnhKey"
|
||||
|
||||
static var isFirstLaunch: Bool {
|
||||
get {
|
||||
let value = UserDefaults.standard.value(forKey: self.firstLaucnhKey) as? Bool ?? true
|
||||
if value {
|
||||
SPWelcomeViewController.updateCurrentVersion()
|
||||
}
|
||||
self.isFirstLaunch = false
|
||||
return value
|
||||
}
|
||||
set {
|
||||
UserDefaults.standard.set(newValue, forKey: self.firstLaucnhKey)
|
||||
}
|
||||
}
|
||||
|
||||
static var hasBeenUpdated: Bool {
|
||||
let userDefaults = UserDefaults.standard
|
||||
var bundleInfo = Bundle.main.infoDictionary!
|
||||
|
||||
if let currentVersion = bundleInfo["CFBundleShortVersionString"] as? String {
|
||||
if let version = userDefaults.value(forKey: self.versionKey) as? String {
|
||||
return !(version == currentVersion)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private static func updateCurrentVersion() {
|
||||
var bundleInfo = Bundle.main.infoDictionary!
|
||||
if let currentVersion = bundleInfo["CFBundleShortVersionString"] as? String {
|
||||
UserDefaults.standard.set(currentVersion, forKey: self.versionKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SPWelcomeData {
|
||||
var title: String = "Welcome to"
|
||||
var subtitle: String = "Sparrow"
|
||||
var description: String = "Simple and native welcome controller for all developers"
|
||||
var changes: String = "Description about this update"
|
||||
var comment: String = "Here you can place comment"
|
||||
var buttonTitle: String = "Continue"
|
||||
var image: UIImage = UIImage()
|
||||
var color: UIColor = SPNativeStyleKit.Colors.blue
|
||||
var backgroundColor: UIColor = UIColor.white
|
||||
var textColor: UIColor = UIColor.black
|
||||
var statusBarStyle: SPStatusBar = .dark
|
||||
var complection: (_ button: SPNativeLargeButton) -> () = { _ in }
|
||||
}
|
||||
+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()
|
||||
}
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ class SPSystemIconView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
var color = SPNativeStyleKit.Colors.blue {
|
||||
var color = SPNativeColors.blue {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+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(
|
||||
|
||||
-91
@@ -1,91 +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 SPImagesLineCollectionView: SPCollectionView {
|
||||
|
||||
fileprivate let imageCellIdentificator: String = "imageCellIdentificator"
|
||||
|
||||
var links: [String] = []
|
||||
var images: [UIImage] = []
|
||||
|
||||
var mode: Mode = .links {
|
||||
didSet {
|
||||
self.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
self.layout.scrollDirection = .horizontal
|
||||
self.dataSource = self
|
||||
|
||||
self.register(SPImageCollectionViewCell.self, forCellWithReuseIdentifier: self.imageCellIdentificator)
|
||||
|
||||
self.layout.cellSideRatio = 1
|
||||
self.layout.heightFactor = 1
|
||||
self.layout.minItemSpace = 10
|
||||
self.layout.maxItemSpace = 10
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
case links
|
||||
case images
|
||||
}
|
||||
}
|
||||
|
||||
extension SPImagesLineCollectionView: UICollectionViewDataSource {
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
|
||||
switch self.mode {
|
||||
case .links:
|
||||
return self.links.count
|
||||
case .images:
|
||||
return self.images.count
|
||||
}
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
let cell = dequeueReusableCell(withReuseIdentifier: self.imageCellIdentificator, for: indexPath) as! SPImageCollectionViewCell
|
||||
|
||||
switch self.mode {
|
||||
case .links:
|
||||
let link = self.links[indexPath.row]
|
||||
if let image = self.fromCahce(link: link) {
|
||||
cell.view.setImage(image: image, animatable: false)
|
||||
} else {
|
||||
cell.view.setImage(link: link) { (image) in
|
||||
self.toCache(link: link, image: image)
|
||||
}
|
||||
}
|
||||
case .images:
|
||||
cell.view.setImage(image: self.images[indexPath.row], animatable: false)
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// 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 SPSectionLabelsView: SPView {
|
||||
|
||||
let titleLabel = SPLabel()
|
||||
let subtitleLabel = SPLabel()
|
||||
let button = SPButton()
|
||||
|
||||
override func commonInit() {
|
||||
super.commonInit()
|
||||
|
||||
self.titleLabel.font = UIFont.system(type: .Bold, size: 23)
|
||||
self.titleLabel.textAlignment = .left
|
||||
self.titleLabel.textColor = UIColor.black
|
||||
self.titleLabel.numberOfLines = 0
|
||||
self.addSubview(self.titleLabel)
|
||||
|
||||
self.subtitleLabel.font = UIFont.system(type: .Regular, size: 17)
|
||||
self.subtitleLabel.textAlignment = .left
|
||||
self.subtitleLabel.textColor = UIColor.black.withAlphaComponent(0.7)
|
||||
self.subtitleLabel.numberOfLines = 0
|
||||
self.addSubview(self.subtitleLabel)
|
||||
|
||||
self.button.titleLabel?.font = UIFont.system(type: .Regular, size: 17)
|
||||
self.button.setTitleColor(SPNativeColors.blue, for: UIControl.State.normal)
|
||||
self.addSubview(self.button)
|
||||
}
|
||||
|
||||
func layout(origin: CGPoint, width: CGFloat) {
|
||||
self.frame.origin = origin
|
||||
self.setWidth(width)
|
||||
self.layoutSubviews()
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.titleLabel.sizeToFit()
|
||||
self.titleLabel.setWidth(self.frame.width)
|
||||
self.titleLabel.frame.origin = CGPoint.zero
|
||||
//self.titleLabel.backgroundColor = UIColor.lightGray
|
||||
|
||||
self.subtitleLabel.sizeToFit()
|
||||
self.subtitleLabel.setWidth(self.frame.width)
|
||||
self.subtitleLabel.frame.origin.x = 0
|
||||
self.subtitleLabel.frame.origin.y = self.titleLabel.frame.bottomYPosition + 3
|
||||
//self.subtitleLabel.backgroundColor = UIColor.darkGray
|
||||
|
||||
self.button.sizeToFit()
|
||||
self.button.frame.bottomXPosition = self.frame.width
|
||||
self.button.center.y = self.titleLabel.center.y
|
||||
//self.button.backgroundColor = UIColor.darkGray
|
||||
|
||||
self.setHeight(self.subtitleLabel.frame.bottomYPosition)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPEmptyTitlesView: SPView {
|
||||
|
||||
let titleLabel = UILabel()
|
||||
let subtitleLabel = UILabel()
|
||||
|
||||
init(title: String, subtitle: String) {
|
||||
self.titleLabel.text = title
|
||||
self.subtitleLabel.text = subtitle
|
||||
super.init()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func commonInit() {
|
||||
super.commonInit()
|
||||
|
||||
self.titleLabel.font = UIFont.system(type: .Bold, size: 29)
|
||||
self.titleLabel.textColor = SPNativeColors.gray
|
||||
self.titleLabel.setCenteringAlignment()
|
||||
self.titleLabel.numberOfLines = 0
|
||||
self.addSubview(self.titleLabel)
|
||||
|
||||
self.subtitleLabel.font = UIFont.system(type: .Regular, size: 17)
|
||||
self.subtitleLabel.textColor = SPNativeColors.gray
|
||||
self.subtitleLabel.setCenteringAlignment()
|
||||
self.subtitleLabel.numberOfLines = 0
|
||||
self.addSubview(self.subtitleLabel)
|
||||
}
|
||||
|
||||
func layout(centerY: CGFloat) {
|
||||
if let superview = self.superview {
|
||||
self.setWidth(superview.frame.width * 0.7)
|
||||
self.layoutSubviews()
|
||||
self.center = CGPoint.init(x: superview.frame.width / 2, y: centerY)
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.titleLabel.frame.origin = .zero
|
||||
self.titleLabel.sizeToFit()
|
||||
self.titleLabel.setWidth(self.frame.width)
|
||||
|
||||
self.subtitleLabel.frame.origin = CGPoint.init(x: 0, y: self.titleLabel.frame.bottomYPosition + 5)
|
||||
self.subtitleLabel.sizeToFit()
|
||||
self.subtitleLabel.setWidth(self.frame.width)
|
||||
|
||||
self.setHeight(self.subtitleLabel.frame.bottomYPosition)
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,21 @@ public class SPFakeBarView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
public var closeButton: closeButtonPlace = .none {
|
||||
didSet {
|
||||
self.leftButton.titleLabel?.font = UIFont.system(type: .Regular, size: 17)
|
||||
self.rightButton.titleLabel?.font = UIFont.system(type: .Regular, size: 17)
|
||||
switch self.closeButton {
|
||||
case .left:
|
||||
self.leftButton.titleLabel?.font = UIFont.system(type: .DemiBold, size: 17)
|
||||
case .right:
|
||||
self.rightButton.titleLabel?.font = UIFont.system(type: .DemiBold, size: 17)
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var titleLabel = UILabel.init()
|
||||
public var subtitleLabel = UILabel.init()
|
||||
public var leftButton = UIButton.init()
|
||||
@@ -130,6 +145,8 @@ public class SPFakeBarView: UIView {
|
||||
self.rightButton.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
|
||||
self.rightButton.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -12).isActive = true
|
||||
|
||||
self.closeButton = .none
|
||||
|
||||
self.setContraints()
|
||||
self.updateStyle()
|
||||
}
|
||||
@@ -197,5 +214,11 @@ public class SPFakeBarView: UIView {
|
||||
self.heightConstraint?.constant = self.height
|
||||
self.updateConstraints()
|
||||
}
|
||||
|
||||
public enum closeButtonPlace {
|
||||
case left
|
||||
case right
|
||||
case none
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// 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 SPFooterActionsView: SPView {
|
||||
|
||||
var sectionLabels = SPSectionLabelsView()
|
||||
private var buttons: [SPFooterActionButton] = []
|
||||
private var separators: [SPSeparatorView] = []
|
||||
|
||||
override func commonInit() {
|
||||
super.commonInit()
|
||||
self.backgroundColor = UIColor.clear
|
||||
self.sectionLabels.titleLabel.text = "Actions"
|
||||
self.addSubview(self.sectionLabels)
|
||||
}
|
||||
|
||||
func addButton(title: String, titleColor: UIColor, target: @escaping ()->()) {
|
||||
let button = SPFooterActionButton()
|
||||
button.setTitle(title)
|
||||
button.setTitleColor(titleColor)
|
||||
button.target { target() }
|
||||
self.buttons.append(button)
|
||||
self.addSubview(button)
|
||||
|
||||
let separator = SPSeparatorView()
|
||||
self.separators.append(separator)
|
||||
self.addSubview(separator)
|
||||
}
|
||||
|
||||
func layout(origin: CGPoint, width: CGFloat) {
|
||||
self.frame.origin = origin
|
||||
self.setWidth(width)
|
||||
self.layoutSubviews()
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.sectionLabels.layout(origin: CGPoint.zero, width: self.frame.width)
|
||||
|
||||
let buttonHeight: CGFloat = 50
|
||||
var yPositionButton: CGFloat = self.sectionLabels.frame.bottomYPosition + 12
|
||||
|
||||
if !self.buttons.isEmpty {
|
||||
for i in 0...(buttons.count - 1) {
|
||||
let button = self.buttons[i]
|
||||
let separator = self.separators[i]
|
||||
|
||||
separator.frame.origin.x = 0
|
||||
separator.frame.origin.y = yPositionButton
|
||||
separator.setWidth(self.frame.width)
|
||||
|
||||
button.frame = CGRect.init(x: 0, y: yPositionButton, width: self.frame.width, height: buttonHeight)
|
||||
|
||||
yPositionButton += buttonHeight
|
||||
}
|
||||
|
||||
self.setHeight(yPositionButton)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SPFooterActionButton: SPButton {
|
||||
|
||||
override func commonInit() {
|
||||
super.commonInit()
|
||||
self.setTitleColor(SPNativeColors.blue)
|
||||
self.titleLabel?.font = UIFont.system(type: .Regular, size: 21)
|
||||
self.contentHorizontalAlignment = .left
|
||||
}
|
||||
}
|
||||
@@ -23,21 +23,12 @@ import UIKit
|
||||
|
||||
public class SPGradientView: SPView {
|
||||
|
||||
var startColor: UIColor = UIColor.white { didSet { self.updateGradient() }}
|
||||
var endColor: UIColor = UIColor.black { didSet { self.updateGradient() }}
|
||||
|
||||
var startColorPoint: CGPoint = CGPoint.zero { didSet { self.updateGradient() }}
|
||||
var endColorPoint: CGPoint = CGPoint.zero { didSet { self.updateGradient() }}
|
||||
|
||||
var gradientLayer: CAGradientLayer = CAGradientLayer()
|
||||
|
||||
public func setStartColorPosition(_ position: Position) {
|
||||
self.startColorPoint = getPointForPosition(position)
|
||||
}
|
||||
|
||||
public func setEndColorPosition(_ position: Position) {
|
||||
self.endColorPoint = getPointForPosition(position)
|
||||
}
|
||||
var startColor = UIColor.white { didSet { self.updateGradient() }}
|
||||
var endColor = UIColor.black { didSet { self.updateGradient() }}
|
||||
var startColorPosition = Position.topLeft { didSet { self.updateGradient() }}
|
||||
var endColorPosition = Position.bottomRight { didSet { self.updateGradient() }}
|
||||
|
||||
override func commonInit() {
|
||||
super.commonInit()
|
||||
@@ -47,8 +38,8 @@ public class SPGradientView: SPView {
|
||||
private func updateGradient() {
|
||||
self.gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
|
||||
self.gradientLayer.locations = [0.0, 1.0]
|
||||
self.gradientLayer.startPoint = self.startColorPoint
|
||||
self.gradientLayer.endPoint = self.endColorPoint
|
||||
self.gradientLayer.startPoint = self.startColorPosition.point
|
||||
self.gradientLayer.endPoint = self.endColorPosition.point
|
||||
}
|
||||
|
||||
override public func layoutSublayers(of layer: CALayer) {
|
||||
@@ -57,57 +48,35 @@ public class SPGradientView: SPView {
|
||||
}
|
||||
|
||||
public enum Position {
|
||||
case TopLeft
|
||||
case TopCenter
|
||||
case TopRight
|
||||
case BottomLeft
|
||||
case BottomCenter
|
||||
case BottomRight
|
||||
case MediumLeft
|
||||
case MediumRight
|
||||
}
|
||||
|
||||
private func getPointForPosition(_ position: Position) -> CGPoint {
|
||||
switch position {
|
||||
case .TopLeft:
|
||||
return CGPoint.init(x: 0, y: 0)
|
||||
case .TopCenter:
|
||||
return CGPoint.init(x: 0.5, y: 0)
|
||||
case .TopRight:
|
||||
return CGPoint.init(x: 1, y: 0)
|
||||
case .BottomLeft:
|
||||
return CGPoint.init(x: 0, y: 1)
|
||||
case .BottomCenter:
|
||||
return CGPoint.init(x: 0.5, y: 1)
|
||||
case .BottomRight:
|
||||
return CGPoint.init(x: 1, y: 1)
|
||||
case .MediumLeft:
|
||||
return CGPoint.init(x: 0, y: 0.5)
|
||||
case .MediumRight:
|
||||
return CGPoint.init(x: 1, y: 0.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SPGradientWithPictureView: SPGradientView {
|
||||
|
||||
var pictureView: UIView? {
|
||||
willSet {
|
||||
if self.pictureView != nil {
|
||||
if self.subviews.contains(self.pictureView!) {
|
||||
self.pictureView?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
didSet {
|
||||
if self.pictureView != nil {
|
||||
self.addSubview(pictureView!)
|
||||
|
||||
case topLeft
|
||||
case topCenter
|
||||
case topRight
|
||||
case bottomLeft
|
||||
case bottomCenter
|
||||
case bottomRight
|
||||
case mediumLeft
|
||||
case mediumRight
|
||||
|
||||
var point: CGPoint {
|
||||
switch self {
|
||||
case .topLeft:
|
||||
return CGPoint.init(x: 0, y: 0)
|
||||
case .topCenter:
|
||||
return CGPoint.init(x: 0.5, y: 0)
|
||||
case .topRight:
|
||||
return CGPoint.init(x: 1, y: 0)
|
||||
case .bottomLeft:
|
||||
return CGPoint.init(x: 0, y: 1)
|
||||
case .bottomCenter:
|
||||
return CGPoint.init(x: 0.5, y: 1)
|
||||
case .bottomRight:
|
||||
return CGPoint.init(x: 1, y: 1)
|
||||
case .mediumLeft:
|
||||
return CGPoint.init(x: 0, y: 0.5)
|
||||
case .mediumRight:
|
||||
return CGPoint.init(x: 1, y: 0.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
self.pictureView?.frame = self.bounds
|
||||
}
|
||||
}
|
||||
|
||||
+15
-12
@@ -21,23 +21,26 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPCollectionContainerCell<ContentView: UIView>: UICollectionViewCell {
|
||||
class SPSeparatorView: SPView {
|
||||
|
||||
let view = ContentView.init()
|
||||
var currentIndexPath: IndexPath?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.backgroundColor = UIColor.clear
|
||||
self.addSubview(view)
|
||||
private var height: CGFloat {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
override func commonInit() {
|
||||
super.commonInit()
|
||||
self.backgroundColor = UIColor.init(hex: "515B66").withAlphaComponent(0.25)
|
||||
self.round = true
|
||||
self.setHeight(self.height)
|
||||
}
|
||||
|
||||
override public func layoutSubviews() {
|
||||
func layout(origin: CGPoint, width: CGFloat) {
|
||||
self.frame.origin = CGPoint.init(x: floor(origin.x), y: floor(origin.y))
|
||||
self.set(width: floor(width), height: self.height)
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.view.setEqualsFrameFromBounds(self)
|
||||
self.setHeight(self.height)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -37,7 +37,7 @@ class SPEmptyProposeLabel: UILabel {
|
||||
private func commonInit() {
|
||||
self.setCenteringAlignment()
|
||||
self.font = UIFont.system(type: .Regular, size: 14)
|
||||
self.textColor = SPNativeStyleKit.Colors.gray
|
||||
self.textColor = SPNativeColors.gray
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ class SPEmptyProposeView: UIView {
|
||||
self.label.numberOfLines = 0
|
||||
self.label.setCenteringAlignment()
|
||||
self.label.font = UIFont.system(type: .Regular, size: 13)
|
||||
self.label.textColor = SPNativeStyleKit.Colors.gray
|
||||
self.label.textColor = SPNativeColors.gray
|
||||
self.addSubview(self.label)
|
||||
|
||||
self.addSubview(self.button)
|
||||
|
||||
+2
-2
@@ -250,7 +250,7 @@ class SPBaseContentTableViewCell: SPTableViewCell {
|
||||
self.descriptionLabel.numberOfLines = 0
|
||||
self.descriptionLabel.font = UIFont.system(type: UIFont.BoldType.Regular, size: 15)
|
||||
self.descriptionLabel.textAlignment = .left
|
||||
self.descriptionLabel.textColor = SPNativeStyleKit.Colors.gray
|
||||
self.descriptionLabel.textColor = SPNativeColors.gray
|
||||
self.contentView.addSubview(self.descriptionLabel)
|
||||
self.descriptionLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
@@ -305,7 +305,7 @@ class SPBaseContentTableViewCell: SPTableViewCell {
|
||||
self.descriptionLabel.text = "Description"
|
||||
self.descriptionLabel.font = UIFont.system(type: UIFont.BoldType.Regular, size: 15)
|
||||
self.descriptionLabel.textAlignment = .left
|
||||
self.descriptionLabel.textColor = SPNativeStyleKit.Colors.gray
|
||||
self.descriptionLabel.textColor = SPNativeColors.gray
|
||||
self.button.setTitle("Button")
|
||||
|
||||
self.imageSide = 63
|
||||
|
||||
-155
@@ -1,155 +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 SPCollectionImagesTableViewCell: SPTableViewCell {
|
||||
|
||||
let titleLabel = UILabel()
|
||||
let subtitleLabel = UILabel()
|
||||
let collectionView = SPImagesLineCollectionView()
|
||||
|
||||
var topSpace: CGFloat = 11 {
|
||||
didSet {
|
||||
self.titleLabelTopConstraint.constant = self.topSpace
|
||||
self.updateConstraints()
|
||||
}
|
||||
}
|
||||
|
||||
var collectionHeight: CGFloat = 63 {
|
||||
didSet {
|
||||
self.collectionHeightConstraint.constant = self.collectionHeight
|
||||
self.updateConstraints()
|
||||
}
|
||||
}
|
||||
|
||||
//constraints
|
||||
private var collectionTopConstraint: NSLayoutConstraint!
|
||||
private var collectionHeightConstraint: NSLayoutConstraint!
|
||||
private var titleLabelTopConstraint: NSLayoutConstraint!
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
let marginGuide = contentView.layoutMarginsGuide
|
||||
|
||||
self.titleLabel.numberOfLines = 0
|
||||
self.titleLabel.font = UIFont.system(type: UIFont.BoldType.Medium, size: 21)
|
||||
self.titleLabel.textAlignment = .left
|
||||
self.titleLabel.textColor = UIColor.black
|
||||
self.contentView.addSubview(self.titleLabel)
|
||||
self.titleLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.titleLabel.leadingAnchor.constraint(equalTo:
|
||||
marginGuide.leadingAnchor, constant: 0).isActive = true
|
||||
self.titleLabel.trailingAnchor.constraint(equalTo:
|
||||
marginGuide.trailingAnchor).isActive = true
|
||||
self.titleLabelTopConstraint = self.titleLabel.topAnchor.constraint(equalTo:
|
||||
marginGuide.topAnchor, constant: 0)
|
||||
self.titleLabelTopConstraint.isActive = true
|
||||
|
||||
self.subtitleLabel.numberOfLines = 3
|
||||
self.subtitleLabel.font = UIFont.system(type: UIFont.BoldType.Regular, size: 15)
|
||||
self.subtitleLabel.textAlignment = .left
|
||||
self.subtitleLabel.textColor = SPNativeStyleKit.Colors.gray
|
||||
self.contentView.addSubview(self.subtitleLabel)
|
||||
self.subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.subtitleLabel.leadingAnchor.constraint(equalTo:
|
||||
self.titleLabel.leadingAnchor).isActive = true
|
||||
self.subtitleLabel.trailingAnchor.constraint(equalTo:
|
||||
marginGuide.trailingAnchor).isActive = true
|
||||
self.subtitleLabel.topAnchor.constraint(equalTo:
|
||||
titleLabel.bottomAnchor, constant: 3).isActive = true
|
||||
|
||||
self.collectionView.backgroundColor = UIColor.clear
|
||||
self.collectionView.showsHorizontalScrollIndicator = false
|
||||
self.collectionView.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.contentView.addSubview(self.collectionView)
|
||||
self.collectionView.leadingAnchor.constraint(equalTo:
|
||||
self.leadingAnchor).isActive = true
|
||||
self.collectionView.trailingAnchor.constraint(equalTo:
|
||||
self.trailingAnchor).isActive = true
|
||||
self.collectionHeightConstraint = self.collectionView.heightAnchor.constraint(equalToConstant: 63)
|
||||
self.collectionHeightConstraint.isActive = true
|
||||
self.collectionTopConstraint = self.collectionView.topAnchor.constraint(equalTo:
|
||||
subtitleLabel.bottomAnchor, constant: 10)
|
||||
self.collectionTopConstraint.isActive = true
|
||||
self.collectionView.bottomAnchor.constraint(equalTo:
|
||||
marginGuide.bottomAnchor, constant: -7).isActive = true
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
self.collectionView.reloadData()
|
||||
self.accessoryType = .none
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.collectionView.contentInset.left = self.contentView.layoutMargins.left
|
||||
self.collectionView.contentInset.right = self.contentView.layoutMargins.right
|
||||
self.accessoryView?.center.y = self.titleLabel.frame.bottomYPosition + self.subtitleLabel.frame.height / 2
|
||||
if self.accessoryView?.frame.origin.y ?? 0 < 5 {
|
||||
self.accessoryView?.center.y = self.frame.height / 2
|
||||
}
|
||||
}
|
||||
|
||||
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
|
||||
var backgroundColor = SPNativeStyleKit.Colors.customGray
|
||||
if let cell = self.collectionView.cellForItem(at: IndexPath.init(row: 0, section: 0)) as? SPImageCollectionViewCell {
|
||||
if let color = cell.view.gradeView.backgroundColor {
|
||||
backgroundColor = color
|
||||
}
|
||||
}
|
||||
|
||||
super.setHighlighted(highlighted, animated: animated)
|
||||
|
||||
for cell in self.collectionView.visibleCells {
|
||||
if let imageCell = cell as? SPImageCollectionViewCell {
|
||||
imageCell.view.gradeView.backgroundColor = backgroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func setSelected(_ selected: Bool, animated: Bool) {
|
||||
var backgroundColor = SPNativeStyleKit.Colors.customGray
|
||||
if let cell = self.collectionView.cellForItem(at: IndexPath.init(row: 0, section: 0)) as? SPImageCollectionViewCell {
|
||||
if let color = cell.view.gradeView.backgroundColor {
|
||||
backgroundColor = color
|
||||
}
|
||||
}
|
||||
|
||||
super.setSelected(selected, animated: animated)
|
||||
|
||||
for cell in self.collectionView.visibleCells {
|
||||
if let imageCell = cell as? SPImageCollectionViewCell {
|
||||
imageCell.view.gradeView.backgroundColor = backgroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -79,7 +79,7 @@ class SPImageTableViewCell: SPTableViewCell {
|
||||
self.subtitleLabel.numberOfLines = 3
|
||||
self.subtitleLabel.font = UIFont.system(type: UIFont.BoldType.Regular, size: 15)
|
||||
self.subtitleLabel.textAlignment = .left
|
||||
self.subtitleLabel.textColor = SPNativeStyleKit.Colors.gray
|
||||
self.subtitleLabel.textColor = SPNativeColors.gray
|
||||
self.contentView.addSubview(self.subtitleLabel)
|
||||
self.subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.subtitleLabel.leadingAnchor.constraint(equalTo:
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ class SPProposeTableViewCell: UITableViewCell {
|
||||
|
||||
self.button.titleLabel?.font = UIFont.system(type: .DemiBold, size: 14)
|
||||
self.button.titleLabel?.numberOfLines = 0
|
||||
self.button.secondColor = SPNativeStyleKit.Colors.blue
|
||||
self.button.secondColor = SPNativeColors.blue
|
||||
self.button.baseColor = UIColor.white
|
||||
self.button.style = .base
|
||||
self.button.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.center
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ class SPFormButtonTableViewCell: UITableViewCell {
|
||||
self.backgroundColor = UIColor.white
|
||||
self.button.setTitle("Button", for: .normal)
|
||||
self.button.backgroundColor = UIColor.clear
|
||||
self.button.setTitleColor(SPNativeStyleKit.Colors.blue)
|
||||
self.button.setTitleColor(SPNativeColors.blue)
|
||||
self.button.titleLabel?.font = UIFont.system(type: .Medium, size: 17)
|
||||
self.selectionStyle = .none
|
||||
self.contentView.addSubview(self.button)
|
||||
|
||||
+2
-2
@@ -34,7 +34,7 @@ class SPFormFeaturedTitleTableViewCell: UITableViewCell {
|
||||
self.titleLabel.textColor = UIColor.black
|
||||
case .smallColorful:
|
||||
self.titleLabel.font = UIFont.system(type: .DemiBold, size: 11)
|
||||
self.titleLabel.textColor = SPNativeStyleKit.Colors.blue
|
||||
self.titleLabel.textColor = SPNativeColors.blue
|
||||
self.titleLabel.text = self.titleLabel.text?.uppercased()
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ class SPFormFeaturedTitleTableViewCell: UITableViewCell {
|
||||
|
||||
self.withButton = false
|
||||
self.button.setTitle("Button Title", for: UIControl.State.normal)
|
||||
self.button.setTitleColor(SPNativeStyleKit.Colors.blue)
|
||||
self.button.setTitleColor(SPNativeColors.blue)
|
||||
self.button.titleLabel?.font = UIFont.system(type: .Medium, size: 17)
|
||||
self.button.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.button.titleLabel?.textAlignment = .right
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ class SPFormLabelTableViewCell: SPTableViewCell {
|
||||
self.descriptionLabel.text = "Description"
|
||||
self.descriptionLabel.numberOfLines = 1
|
||||
self.descriptionLabel.font = UIFont.system(type: .Regular, size: 17)
|
||||
self.descriptionLabel.textColor = SPNativeStyleKit.Colors.gray
|
||||
self.descriptionLabel.textColor = SPNativeColors.gray
|
||||
self.contentView.addSubview(self.descriptionLabel)
|
||||
|
||||
self.stopLoading(animated: false)
|
||||
|
||||
@@ -21,30 +21,14 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPVibration {
|
||||
struct SPVibration {
|
||||
|
||||
private let generator = UINotificationFeedbackGenerator()
|
||||
|
||||
func prepare() {
|
||||
self.generator.prepare()
|
||||
}
|
||||
|
||||
func impact(_ mode: Mode) {
|
||||
|
||||
switch mode {
|
||||
case .success:
|
||||
self.generator.notificationOccurred(UINotificationFeedbackGenerator.FeedbackType.success)
|
||||
case .warning:
|
||||
self.generator.notificationOccurred(UINotificationFeedbackGenerator.FeedbackType.warning)
|
||||
case .error:
|
||||
self.generator.notificationOccurred(UINotificationFeedbackGenerator.FeedbackType.error)
|
||||
}
|
||||
}
|
||||
private init() {}
|
||||
}
|
||||
|
||||
extension SPVibration {
|
||||
|
||||
static func impact(_ mode: Mode) {
|
||||
static func impact(system mode: SystemMode) {
|
||||
let generator = UINotificationFeedbackGenerator()
|
||||
|
||||
switch mode {
|
||||
@@ -72,7 +56,7 @@ extension SPVibration {
|
||||
}
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
enum SystemMode {
|
||||
case error
|
||||
case success
|
||||
case warning
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>StorkController</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
@@ -22,8 +24,6 @@
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import UIKit
|
||||
|
||||
class ModalTableViewController: UIViewController {
|
||||
|
||||
let navBar = SPFakeBarView(style: .stork)
|
||||
let tableView = UITableView()
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent }
|
||||
|
||||
private var data = ["Assembly", "C", "C++", "Java", "JavaScript", "Php", "Python", "Swift", "Kotlin", "Assembly", "C", "C++", "Java", "JavaScript", "Php", "Python", "Objective-C", "Swift", "Kotlin", "Assembly", "C", "C++", "Java", "JavaScript", "Php", "Python", "Objective-C"]
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
self.view.backgroundColor = UIColor.white
|
||||
self.modalPresentationCapturesStatusBarAppearance = true
|
||||
|
||||
self.tableView.delegate = self
|
||||
self.tableView.dataSource = self
|
||||
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
|
||||
self.tableView.contentInset.top = self.navBar.height
|
||||
self.tableView.scrollIndicatorInsets.top = self.navBar.height
|
||||
self.tableView.contentInset.bottom = self.safeArea.bottom
|
||||
self.tableView.scrollIndicatorInsets.bottom = self.safeArea.bottom
|
||||
self.view.addSubview(self.tableView)
|
||||
|
||||
self.navBar.titleLabel.text = "Table"
|
||||
self.navBar.leftButton.setTitle("Cancel", for: .normal)
|
||||
self.navBar.leftButton.addTarget(self, action: #selector(self.dismissAction), for: .touchUpInside)
|
||||
self.view.addSubview(self.navBar)
|
||||
|
||||
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) {
|
||||
self.tableView.frame = CGRect.init(origin: CGPoint.zero, size: size)
|
||||
}
|
||||
|
||||
@objc func dismissAction() {
|
||||
self.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
extension ModalTableViewController: UITableViewDataSource {
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
|
||||
cell.textLabel?.text = data[indexPath.row]
|
||||
cell.transform = .identity
|
||||
return cell
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return data.count
|
||||
}
|
||||
}
|
||||
|
||||
extension ModalTableViewController: UITableViewDelegate {
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
SPStorkController.scrollViewDidScroll(scrollView)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import UIKit
|
||||
|
||||
class ModalViewController: UIViewController {
|
||||
|
||||
let navBar = SPFakeBarView(style: .stork)
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent }
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
self.view.backgroundColor = UIColor.white
|
||||
self.modalPresentationCapturesStatusBarAppearance = true
|
||||
|
||||
self.navBar.titleLabel.text = "View"
|
||||
self.navBar.leftButton.setTitle("Cancel", for: .normal)
|
||||
self.navBar.leftButton.addTarget(self, action: #selector(self.dismissAction), for: .touchUpInside)
|
||||
self.view.addSubview(self.navBar)
|
||||
}
|
||||
|
||||
@objc func dismissAction() {
|
||||
self.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import UIKit
|
||||
|
||||
class ViewController: UIViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
let tap = UITapGestureRecognizer(target: self, action: #selector(self.viewWasTapped))
|
||||
view.addGestureRecognizer(tap)
|
||||
|
||||
print("Tap anymore for present modal controller")
|
||||
}
|
||||
|
||||
@objc func viewWasTapped() {
|
||||
let modal = ModalViewController()
|
||||
let transitionDelegate = SPStorkTransitioningDelegate()
|
||||
modal.transitioningDelegate = transitionDelegate
|
||||
modal.modalPresentationStyle = .custom
|
||||
present(modal, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
class ModalViewController: UIViewController {
|
||||
|
||||
let navBar = SPFakeBarView(style: .stork)
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
self.view.backgroundColor = UIColor.white
|
||||
self.modalPresentationCapturesStatusBarAppearance = true
|
||||
|
||||
let tap = UITapGestureRecognizer(target: self, action: #selector(self.dismissAction))
|
||||
view.addGestureRecognizer(tap)
|
||||
|
||||
self.navBar.titleLabel.text = "Title"
|
||||
self.navBar.leftButton.setTitle("Cancel", for: .normal)
|
||||
self.navBar.leftButton.addTarget(self, action: #selector(self.dismissAction), for: .touchUpInside)
|
||||
self.view.addSubview(self.navBar)
|
||||
}
|
||||
|
||||
@objc func dismissAction() {
|
||||
self.dismiss()
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return .lightContent
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,30 @@
|
||||
<img src="https://rawcdn.githack.com/IvanVorobei/SPStorkController/90c836ec5649e77fb44ff727d7dad96d2009f3d8/Resources/SPStorkController - Name.svg"/>
|
||||
<img src="https://github.com/IvanVorobei/SPStorkController/blob/master/Resources/Banner.svg"/>
|
||||
|
||||
Modal controller as in mail or Apple music application. Similar animation and transition. I tried to repeat all the animations, corner radius and frames. The controller supports gestures and Navigation Bar & work with ScrollView.
|
||||
Modal controller like in Mail or Apple Music application. Similar animation and transition. I tried to recreate all the animations, corner radius and frames. Controller supports gestures and Navigation Bar and works with ScrollView. You can watch [how to use pod tutorial](https://youtu.be/wOTNGswT2-0) on YouTube.
|
||||
|
||||
Preview GIF loading `3mb`. Please, wait
|
||||
Preview GIF is loading `3mb`. Please, wait.
|
||||
|
||||
<img src="https://rawcdn.githack.com/IvanVorobei/SPStorkController/0acd51bbe76ef48611e1bdd408aebb9c7d9b0ae6/resources/gif-mockup.gif" width="500">
|
||||
<img src="https://github.com/IvanVorobei/SPStorkController/blob/master/Resources/Preview.gif" width="500">
|
||||
|
||||
<img src="https://rawcdn.githack.com/IvanVorobei/SPStorkController/4b1c91dacc4d3f901fcab5c7efdff256a40c4381/Resources/SPStorkController - Donate.svg"/>
|
||||
You can download example [from AppStore](https://itunes.apple.com/app/id1446635818). Also in the app you can donate me a cup of coffee. If you want to buy source code of the app shown on the GIF above, please go to [xcode-shop.com](https://xcode-shop.com). Price: $200.
|
||||
|
||||
The project is absolutely free, but but it takes time to support and update it. Your support is very motivating and very important. I often receive emails asking me to update or add functionality. [Small donate](https://money.yandex.ru/to/410012745748312) for a cup of coffee helps to develop the project and make it better
|
||||
<img src="https://github.com/IvanVorobei/SPStorkController/blob/master/Resources/Shop.svg"/>
|
||||
|
||||
<img src="https://rawcdn.githack.com/IvanVorobei/SPStorkController/4b1c91dacc4d3f901fcab5c7efdff256a40c4381/Resources/SPStorkController - Donate.svg"/>
|
||||
I have a store where I sell applications and modules for Xcode projects. You can find source codes of applications or custom animations / UI. I regularly update the code. Visit my website to see all items for sale: [xcode-shop.com](https://xcode-shop.com). On the website you can find previews and for some items links to AppStore.
|
||||
|
||||
<img src="https://github.com/IvanVorobei/SPStorkController/blob/master/Resources/Shop.svg"/>
|
||||
|
||||
## Requirements
|
||||
Swift 4.2. Ready for use on iOS 10+
|
||||
|
||||
## Integration
|
||||
Drop in `Source/SPStorkController` folder to your Xcode project. Make sure to enable `Copy items if needed` and `Create groups`.
|
||||
Put `Source/SPStorkController` folder in your Xcode project. Make sure to enable `Copy items if needed` and `Create groups`.
|
||||
|
||||
Or via CocoaPods:
|
||||
```ruby
|
||||
pod 'SPStorkController'
|
||||
```
|
||||
|
||||
and import library in class:
|
||||
```swift
|
||||
import SPStorkController
|
||||
```
|
||||
|
||||
## How to use
|
||||
Create controller and set `transitioningDelegate` to `SPStorkTransitioningDelegate` object. Use `present` or `dismiss` functions:
|
||||
|
||||
@@ -49,29 +46,83 @@ class ViewController: UIViewController {
|
||||
}
|
||||
```
|
||||
|
||||
Please, not init `SPStorkTransitioningDelegate` like here:
|
||||
Please, do not init `SPStorkTransitioningDelegate` like this:
|
||||
|
||||
```swift
|
||||
controller.transitioningDelegate = SPStorkTransitioningDelegate()
|
||||
```
|
||||
|
||||
You get error about weak property.
|
||||
You will get an error about weak property.
|
||||
|
||||
### Parametrs
|
||||
### Video Tutorial
|
||||
|
||||
- Parametr `isSwipeToDismissEnabled` enable dissmiss by swipe gester. Defualt is `true`:
|
||||
You can see how to use `SPStorkController` and how to customize it [in this video](https://youtu.be/wOTNGswT2-0). For English speakers I’ve added subtitles, don’t forget to turn them on:
|
||||
|
||||
[](https://youtu.be/wOTNGswT2-0)
|
||||
|
||||
On my [YouTube channel](http://youtube.com/ivanvorobei) you can find videos about Xcode and Design. I would appreciate it if you like and subscribe. If you do not want to watch the video, I wrote a small wiki below.
|
||||
|
||||
### Light StatusBar
|
||||
|
||||
To set `light` status bar for presented controller, use `preferredStatusBarStyle` property. Also set `modalPresentationCapturesStatusBarAppearance`. See example:
|
||||
|
||||
```swift
|
||||
import UIKit
|
||||
|
||||
class ModalViewController: UIViewController {
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return .lightContent
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
self.modalPresentationCapturesStatusBarAppearance = true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
- Parameter `isSwipeToDismissEnabled` enables dismissal by swipe gesture. Default is `true`:
|
||||
|
||||
```swift
|
||||
transitionDelegate.isSwipeToDismissEnabled = true
|
||||
```
|
||||
|
||||
- Parametr `showIndicator` show or hide top arrow indicator. Defualt is `true`:
|
||||
- Parameter `isTapAroundToDismissEnabled` enables dismissal by tapping parent controller. Default is `true`:
|
||||
|
||||
```swift
|
||||
transitionDelegate.isTapAroundToDismissEnabled = true
|
||||
```
|
||||
|
||||
- Parameter `showIndicator` shows or hides top arrow indicator. Default is `true`:
|
||||
```swift
|
||||
transitionDelegate.showIndicator = true
|
||||
```
|
||||
|
||||
- Parameter `indicatorColor` for customize color of arrow. Default is `gray`:
|
||||
```swift
|
||||
transitionDelegate.indicatorColor = UIColor.white
|
||||
```
|
||||
|
||||
- Parameter `customHeight` sets custom height for modal controller. Default is `nil`:
|
||||
```swift
|
||||
transitionDelegate.customHeight = 350
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
|
||||
The project uses a snapshot of the screen in order to avoid compatibility and customization issues. Before controller presentation, a snapshot of the parent view is made, and size and position are changed for the snapshot. Sometimes you will need to update the screenshot of the parent view, for that use static func:
|
||||
|
||||
```swift
|
||||
SPStorkController.updatePresentingController(modal: controller)
|
||||
```
|
||||
|
||||
and pass the controller, which is modal and uses `SPStorkTransitioningDelegate`
|
||||
|
||||
### Add Navigation Bar
|
||||
You may want to add a navigation bar to your modal controller. Since it became impossible to change or customize the native controller in swift 4 (I couldn’t even find a way to change the height of bar), I completely create navigation bar. Visually, it looks real, but it doesn’t execute the necessary functions:
|
||||
You may want to add a navigation bar to your modal controller. Since it became impossible to change or customize the native controller in swift 4 (I couldn’t even find a way to change the height of the bar), I had to recreate navigation bar from the ground up. Visually it looks real, but it doesn’t execute the necessary functions:
|
||||
|
||||
```swift
|
||||
import UIKit
|
||||
@@ -94,19 +145,19 @@ class ModalController: UIViewController {
|
||||
}
|
||||
```
|
||||
|
||||
You only need to add a navigation bar to the main view, it will automatically layout. Use style `.stork` in init `SPFakeBarView`. It is image preview with Navigation Bar and without it:
|
||||
You only need to add a navigation bar to the main view, it will automatically layout. Use style `.stork` in init of `SPFakeBarView`. Here is visual preview with Navigation Bar and without it:
|
||||
|
||||
<img src="https://rawcdn.githack.com/IvanVorobei/SPStorkController/916cfef888b3e70ca45d1b8b26fba1947421632b/Recources/SPStorkController - Banner.jpg"/>
|
||||
<img src="https://github.com/IvanVorobei/SPStorkController/blob/master/Resources/Navigation%20Bar.jpg"/>
|
||||
|
||||
For use `SPFakeBarView` you should install [SparrowKit](https://github.com/IvanVorobei/SparrowKit) pod:
|
||||
To use `SPFakeBarView` you need to install [SparrowKit](https://github.com/IvanVorobei/SparrowKit) pod:
|
||||
|
||||
```ruby
|
||||
pod 'SparrowKit'
|
||||
```
|
||||
|
||||
### Work with UIScrollView
|
||||
### Working with UIScrollView
|
||||
|
||||
If you use `UIScrollView` (or UITableView & UICollectionView) on your controller, I recommend making it more interactive. When the scroll reaches the top position, the controller will interactively drag down, simulating a closing animation. To do this, set the delegate and in the function `scrollViewDidScroll` call:
|
||||
If you use `UIScrollView` (or UITableView & UICollectionView) on your controller, I recommend making it more interactive. When scrolling reaches the top position, the controller will interactively drag down, simulating a closing animation. To do this, set the delegate and in the function `scrollViewDidScroll` call:
|
||||
|
||||
```swift
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
@@ -114,25 +165,51 @@ func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
}
|
||||
```
|
||||
|
||||
### Working with UITableView & UICollectionView
|
||||
|
||||
Working with a collections classes is not difficult. In the `Example` folder you can find an implementation. However, I will give a couple of tips for making the table look better.
|
||||
|
||||
Firstly, if you use `SPFakeBarView`, don't forget to set top insets for content & scroll indicator. Also, I recommend setting bottom insets:
|
||||
|
||||
```swift
|
||||
tableView.contentInset.top = self.navBar.height
|
||||
tableView.scrollIndicatorInsets.top = self.navBar.height
|
||||
|
||||
tableView.contentInset.bottom = self.safeAreaInsets.bottom
|
||||
tableView.scrollIndicatorInsets.bottom = self.safeAreaInsets.bottom
|
||||
```
|
||||
|
||||
Please, also use `SPStorkController.scrollViewDidScroll()` function in delegate for more interactiveness with your collection or table view
|
||||
|
||||
### Modal presentation of different controller
|
||||
|
||||
If you want to present modal controller on SPStorkController, please set:
|
||||
|
||||
```swift
|
||||
controller.modalPresentationStyle = .custom
|
||||
```
|
||||
|
||||
It’s needed for correct presentation and dismissal of all modal controllers.
|
||||
|
||||
## My projects
|
||||
|
||||
Here I would like to offer you my other projects
|
||||
Here I would like to offer you my other projects.
|
||||
|
||||
### SPPermission
|
||||
Project [SPPermission](https://github.com/IvanVorobei/SPPermission) for managing permissions with the customizable visual effects. Beautiful dialog increases the chance of approval (which is important when we request notification). Simple control of this module saves you hours of development. You can start using project with just two lines of code and easy customization!
|
||||
Project [SPPermission](https://github.com/IvanVorobei/SPPermission) for managing permissions with customizable visual effects. Beautiful dialog increases the chance of approval (which is important when we request notification). Simple control of this module saves you hours of development. You can start using project with just two lines of code and easy customization!
|
||||
|
||||
<img src="https://rawcdn.githack.com/IvanVorobei/RequestPermission/fb53d20f152a3e76e053e6af529306611fb794f0/resources/request-permission - mockup_preview.gif" width="500">
|
||||
<img src="https://github.com/IvanVorobei/SPPermission/blob/master/Resources/Preview.gif" width="500">
|
||||
|
||||
### SparrowKit
|
||||
The `SPStorkController` in the past was part of [SparrowKit](https://github.com/IvanVorobei/SparrowKit) library. In library you can find many useful extensions & classes. For install via CocoaPods use:
|
||||
`SPStorkController` was formerly a part of [SparrowKit](https://github.com/IvanVorobei/SparrowKit) library. In the library you can find many useful extensions & classes. To install via CocoaPods use:
|
||||
|
||||
```ruby
|
||||
pod 'SparrowKit'
|
||||
```
|
||||
|
||||
## License
|
||||
`SPStorkController` is released under the MIT license. Check LICENSE.md for details
|
||||
`SPStorkController` is released under the MIT license. Check `LICENSE.md` for details.
|
||||
|
||||
## Contact
|
||||
If you need develop application or UI, write me to hello@ivanvorobei.by. I am develop design and ios apps. I am use `swift`. For request more functionality, you should create a new issue.
|
||||
My apps in AppStore: [first account](https://itunes.apple.com/us/developer/polina-zubarik/id1434528595) & [second account](https://itunes.apple.com/us/developer/mikalai-varabei/id1435792103)
|
||||
If you need any application or UI to be developed, message me at hello@ivanvorobei.by. I develop iOS apps and create designs, too. I use `swift` for development. To request more functionality, you should create a new issue.
|
||||
Here are my apps in AppStore: [first account](https://itunes.apple.com/us/developer/polina-zubarik/id1434528595) & [second account](https://itunes.apple.com/us/developer/mikalai-varabei/id1435792103).
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 28 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.6 MiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 7.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 780 KiB |
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "SPStorkController"
|
||||
s.version = "1.1.6"
|
||||
s.version = "1.2.4"
|
||||
s.summary = "Modal controller as mail or Apple music application"
|
||||
s.homepage = "https://github.com/IvanVorobei/SPStorkController"
|
||||
s.source = { :git => "https://github.com/IvanVorobei/SPStorkController.git", :tag => s.version }
|
||||
|
||||
@@ -41,6 +41,18 @@ public struct SPStorkController {
|
||||
}
|
||||
}
|
||||
|
||||
static public func updatePresentingController(parent controller: UIViewController) {
|
||||
if let presentationController = controller.presentedViewController?.presentationController as? SPStorkPresentationController {
|
||||
presentationController.updatePresentingController()
|
||||
}
|
||||
}
|
||||
|
||||
static public func updatePresentingController(modal controller: UIViewController) {
|
||||
if let presentationController = controller.presentationController as? SPStorkPresentationController {
|
||||
presentationController.updatePresentingController()
|
||||
}
|
||||
}
|
||||
|
||||
static private func controller(for view: UIView) -> UIViewController? {
|
||||
var nextResponder = view.next
|
||||
while nextResponder != nil && !(nextResponder! is UIViewController) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user