Compare commits
217 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0463bb7a8e | |||
| 12f8e2bc0d | |||
| 578b3dbb41 | |||
| 223ec2d2c0 | |||
| a0ca7e9e2a | |||
| f0a1914145 | |||
| 4355f5eb09 | |||
| 6b080f503d | |||
| c23ed83bf0 | |||
| 8b1c77d743 | |||
| 915ef85cff | |||
| dc1050be2a | |||
| 99ba8bf657 | |||
| 2b7e4198cf | |||
| 0969cceac8 | |||
| f349dea851 | |||
| b8e8f8d0ce | |||
| ee5f9ee9de | |||
| 8348cd6396 | |||
| 9f4c1be56d | |||
| de779aebef | |||
| 1785822242 | |||
| 1514ea5481 | |||
| ec81b9f5dd | |||
| 7381066b36 | |||
| bc998622eb | |||
| 47fd132451 | |||
| f0c45211b2 | |||
| 2952d5d559 | |||
| a6a5995402 | |||
| 8e476c702f | |||
| 5f7cc4c7b3 | |||
| d8d4e1e339 | |||
| 97c8e8aa32 | |||
| 7be93d548e | |||
| 0f78641fdb | |||
| fd909a3ad6 | |||
| 2bb0c190b3 | |||
| 1ecc16c953 | |||
| 4698fbbe99 | |||
| 03df02d214 | |||
| cbc46d4db4 | |||
| 552f722a88 | |||
| 19caee5ccc | |||
| 2b5d20f26d | |||
| a6913a46a2 | |||
| fab0c7a274 | |||
| d04463f251 | |||
| d37aace4ec | |||
| 33d9b98d7d | |||
| 0363923e1b | |||
| 877108b08b | |||
| 7b25a6b878 | |||
| b69ae3ff7e | |||
| 4eac2276d4 | |||
| 38a6a10280 | |||
| aae897fc68 | |||
| 438811dbe5 | |||
| 7ea761b26e | |||
| 5c5457a09b | |||
| cdccba8929 | |||
| 196beb7592 | |||
| 91fe62e899 | |||
| bab71b560d | |||
| bb11bcd528 | |||
| d1f25b147f | |||
| 3eb459eab5 | |||
| 9060761733 | |||
| d7c5bd193c | |||
| 43a03de8d9 | |||
| 621bdb3e05 | |||
| d50ba88425 | |||
| 08b8422c26 | |||
| 9d13d508c6 | |||
| 6ae45f0568 | |||
| e5dbab65e1 | |||
| 07827b28a9 | |||
| 1dbd0ad54b | |||
| 49f2429633 | |||
| ef0a87e777 | |||
| b23a0b14e3 | |||
| a05b878af4 | |||
| 912c2d3903 | |||
| 3716912bc5 | |||
| ec9e174e14 | |||
| 6b98bf40c2 | |||
| 1167ca0a0d | |||
| c8c565d4ec | |||
| be0be7585b | |||
| c241629ea6 | |||
| 46afa63dbd | |||
| 682ab6127f | |||
| 4d70e9ca26 | |||
| 7091a625f3 | |||
| 5a83c3d1f3 | |||
| ad0c6eaf62 | |||
| 1f96f32980 | |||
| 03b38dfa0e | |||
| 2c35358336 | |||
| 18626df942 | |||
| 1a716980c0 | |||
| b5cf71046f | |||
| 328d3739d6 | |||
| 2a858aa309 | |||
| f2937f6830 | |||
| c274892031 | |||
| 745968a053 | |||
| 4c90c708b7 | |||
| 871e96c1b9 | |||
| ad26638886 | |||
| 0306b46949 | |||
| 0f038463ed | |||
| ae01552b84 | |||
| 272bff5584 | |||
| 2adb5b4c88 | |||
| f930141a04 | |||
| 615fb4fcbc | |||
| 10dafb3853 | |||
| 7505c48033 | |||
| 35770b7362 | |||
| d478decba7 | |||
| 51bb03327c | |||
| f23eacf2ad | |||
| 04c9b77c75 | |||
| e7e83b5ad2 | |||
| 5911771ec9 | |||
| 6c1070848f | |||
| c18e8b8faf | |||
| 1d03424ab4 | |||
| 66556da572 | |||
| 6ebcfa59e9 | |||
| f6a2c31e2f | |||
| be3eb3f3a7 | |||
| a821389fee | |||
| e057aadaf1 | |||
| 4c95e0ed3f | |||
| 71708b43c3 | |||
| 66d7775d01 | |||
| 298e0fdfc0 | |||
| 65070bce00 | |||
| 2210afc7a7 | |||
| 94a8b06dde | |||
| 621e19cf78 | |||
| 6ae5e4f73d | |||
| 57d927bc49 | |||
| 1817c81bcc | |||
| 02e77c6074 | |||
| c922a8f522 | |||
| 86e7b3458d | |||
| a7ffa44434 | |||
| 96cc6b021a | |||
| 398da17b4a | |||
| 5e76f9f4d0 | |||
| a20a025a5b | |||
| 93e5b6f9de | |||
| 9d280b9b51 | |||
| 3d486c8f4b | |||
| d8fc226c9a | |||
| 7fd5eb41a6 | |||
| 263996e39d | |||
| b36cc7720d | |||
| f01ff0e904 | |||
| 92f29009c4 | |||
| b9813933b9 | |||
| ef0169a429 | |||
| 75766e6b7f | |||
| 9e8ed72013 | |||
| 29d6f3af93 | |||
| afa7e48e4c | |||
| b4c022e889 | |||
| 3c6e7e2c9c | |||
| 16e4685963 | |||
| e8e0e22259 | |||
| 19551282e6 | |||
| bc66345c6a | |||
| 73152ed8df | |||
| 431e9f58d5 | |||
| c3d75dac5e | |||
| cf9e7501d2 | |||
| 182ee5f0f0 | |||
| b2b37e717c | |||
| c0ea9d29a1 | |||
| cb55b4861a | |||
| 6a0aa725f7 | |||
| aebe62a9f2 | |||
| a0c0bf6885 | |||
| c0be873778 | |||
| 7fa550f79c | |||
| 6a22bb178b | |||
| 8993c2011d | |||
| 70199b65ee | |||
| 99ed2e84e1 | |||
| 6d8bf7c70a | |||
| a21dd49806 | |||
| 2cb195fcab | |||
| 4c78642040 | |||
| 73d297de19 | |||
| 1d167c9506 | |||
| 0b07e8e2cb | |||
| 8500571554 | |||
| 13134f2748 | |||
| 6e1a5c85b3 | |||
| eab9045cb5 | |||
| f567582b4d | |||
| 30365fde24 | |||
| 6b8fa51706 | |||
| 1f1aa2ea8d | |||
| 914ce0f689 | |||
| 5dfab81504 | |||
| 9270185bf6 | |||
| 43bc6c27ee | |||
| 551ccf3bf1 | |||
| 0488521869 | |||
| 9eae918f9d | |||
| 28ed6b6ebc | |||
| b783329b69 | |||
| 84256152dc |
@@ -0,0 +1,8 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: ivanvorobei
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
custom: # https://xcode-shop.com
|
||||
@@ -1 +1,39 @@
|
||||
# Mac OS X
|
||||
.DS_Store
|
||||
|
||||
# Xcode
|
||||
## Build generated
|
||||
build/
|
||||
DerivedData
|
||||
|
||||
## Various settings
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
|
||||
## Other
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
*.xcuserstate
|
||||
*.xcscmblueprint
|
||||
|
||||
## Obj-C/Swift specific
|
||||
*.hmap
|
||||
*.ipa
|
||||
|
||||
## Playgrounds
|
||||
timeline.xctimeline
|
||||
playground.xcworkspace
|
||||
|
||||
# Swift Package Manager
|
||||
.build/
|
||||
.swiftpm/
|
||||
|
||||
# Carthage
|
||||
Carthage/Build
|
||||
@@ -14,28 +14,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
return true
|
||||
}
|
||||
|
||||
func applicationWillResignActive(_ application: UIApplication) {
|
||||
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
||||
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
|
||||
}
|
||||
|
||||
func applicationDidEnterBackground(_ application: UIApplication) {
|
||||
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
|
||||
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
||||
}
|
||||
|
||||
func applicationWillEnterForeground(_ application: UIApplication) {
|
||||
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
|
||||
}
|
||||
|
||||
func applicationDidBecomeActive(_ application: UIApplication) {
|
||||
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ application: UIApplication) {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
}
|
||||
|
||||
func launch(rootViewController: UIViewController) {
|
||||
let frame = UIScreen.main.bounds
|
||||
self.window = UIWindow(frame: frame)
|
||||
|
||||
@@ -1,93 +1,111 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "icon_20pt@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "icon_20pt@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "icon_29pt@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "icon_29pt@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "icon_40pt@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "icon_40pt@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "icon_60pt@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "icon_60pt@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "icon_20pt.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "icon_20pt@2x-1.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "icon_29pt.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "icon_29pt@2x-1.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "icon_40pt.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "icon_40pt@2x-1.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "icon_76pt.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "icon_76pt@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "83.5x83.5",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "icon_83.5@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ios-marketing",
|
||||
"size" : "1024x1024",
|
||||
"idiom" : "ios-marketing",
|
||||
"filename" : "Icon.png",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
|
||||
|
After Width: | Height: | Size: 483 KiB |
|
After Width: | Height: | Size: 685 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -28,6 +28,8 @@ class Controller: UIViewController {
|
||||
@objc func presentModalViewController() {
|
||||
let modal = ModalViewController()
|
||||
let transitionDelegate = SPStorkTransitioningDelegate()
|
||||
transitionDelegate.storkDelegate = self
|
||||
transitionDelegate.confirmDelegate = modal
|
||||
modal.transitioningDelegate = transitionDelegate
|
||||
modal.modalPresentationStyle = .custom
|
||||
self.present(modal, animated: true, completion: nil)
|
||||
@@ -36,8 +38,21 @@ class Controller: UIViewController {
|
||||
@objc func presentModalTableViewController() {
|
||||
let modal = ModalTableViewController()
|
||||
let transitionDelegate = SPStorkTransitioningDelegate()
|
||||
transitionDelegate.storkDelegate = self
|
||||
transitionDelegate.confirmDelegate = modal
|
||||
modal.transitioningDelegate = transitionDelegate
|
||||
modal.modalPresentationStyle = .custom
|
||||
self.present(modal, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension Controller: SPStorkControllerDelegate {
|
||||
|
||||
func didDismissStorkByTap() {
|
||||
print("SPStorkControllerDelegate - didDismissStorkByTap")
|
||||
}
|
||||
|
||||
func didDismissStorkBySwipe() {
|
||||
print("SPStorkControllerDelegate - didDismissStorkBySwipe")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public enum SPFakeBarNavigationStyle {
|
||||
|
||||
case large
|
||||
case small
|
||||
case stork
|
||||
case noContent
|
||||
}
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPFakeBarView: UIView {
|
||||
open class SPFakeBarView: UIView {
|
||||
|
||||
public var style: SPNavigationTitleStyle = .small {
|
||||
public var style: SPFakeBarNavigationStyle = .small {
|
||||
didSet {
|
||||
self.updateStyle()
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public class SPFakeBarView: UIView {
|
||||
private var settedHeight: CGFloat = 0
|
||||
public var height: CGFloat {
|
||||
get {
|
||||
return (self.settedHeight) + (self.addStatusBarHeight ? UIViewController.statusBarHeight : 0)
|
||||
return (self.settedHeight) + (self.addStatusBarHeight ? UIApplication.shared.statusBarFrame.height : 0)
|
||||
}
|
||||
set {
|
||||
self.settedHeight = newValue
|
||||
@@ -47,22 +47,24 @@ public class SPFakeBarView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
public var elementsColor: UIColor = UINavigationController.elementsColor {
|
||||
public var elementsColor: UIColor = SPFakeBarView.navigationElementsColor {
|
||||
didSet {
|
||||
self.leftButton.setTitleColor(self.elementsColor)
|
||||
self.rightButton.setTitleColor(self.elementsColor)
|
||||
self.leftButton.setTitleColor(self.elementsColor, for: .normal)
|
||||
self.leftButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
|
||||
self.rightButton.setTitleColor(self.elementsColor, for: .normal)
|
||||
self.rightButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
|
||||
}
|
||||
}
|
||||
|
||||
public var closeButtonPossition: CloseButtonPosition = .none {
|
||||
didSet {
|
||||
self.leftButton.titleLabel?.font = UIFont.system(weight: .regular, size: 17)
|
||||
self.rightButton.titleLabel?.font = UIFont.system(weight: .regular, size: 17)
|
||||
self.leftButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .regular)
|
||||
self.rightButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .regular)
|
||||
switch self.closeButtonPossition {
|
||||
case .left:
|
||||
self.leftButton.titleLabel?.font = UIFont.system(weight: .demiBold, size: 17)
|
||||
self.leftButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
|
||||
case .right:
|
||||
self.rightButton.titleLabel?.font = UIFont.system(weight: .demiBold, size: 17)
|
||||
self.rightButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
@@ -74,21 +76,25 @@ public class SPFakeBarView: UIView {
|
||||
public var leftButton = UIButton.init()
|
||||
public var rightButton = UIButton.init()
|
||||
|
||||
public let separatorView = UIView()
|
||||
public let blurView: UIVisualEffectView = {
|
||||
let effect = UIBlurEffect(style: .extraLight)
|
||||
return UIVisualEffectView.init(effect: effect)
|
||||
}()
|
||||
|
||||
private var titleBottomConstraint: NSLayoutConstraint?
|
||||
private var heightConstraint: NSLayoutConstraint?
|
||||
private var topConstraint: NSLayoutConstraint?
|
||||
private var leadingConstraint: NSLayoutConstraint?
|
||||
private var trailingConstraint: NSLayoutConstraint?
|
||||
private let blurView = UIVisualEffectView.init(style: .extraLight)
|
||||
private let separatorView = UIView()
|
||||
|
||||
public init(style: SPNavigationTitleStyle) {
|
||||
public init(style: SPFakeBarNavigationStyle) {
|
||||
super.init(frame: CGRect.zero)
|
||||
self.style = style
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
public required init?(coder aDecoder: NSCoder) {
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
self.style = .small
|
||||
self.commonInit()
|
||||
@@ -105,7 +111,7 @@ public class SPFakeBarView: UIView {
|
||||
self.blurView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
|
||||
|
||||
self.addSubview(self.separatorView)
|
||||
self.separatorView.backgroundColor = UIColor.init(hex: "BFBFBF")
|
||||
self.separatorView.backgroundColor = UIColor.init(red: 191 / 255.0, green: 191 / 255.0, blue: 191 / 255.0, alpha: 1)
|
||||
self.separatorView.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.separatorView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
|
||||
self.separatorView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
|
||||
@@ -120,25 +126,27 @@ public class SPFakeBarView: UIView {
|
||||
self.titleBottomConstraint?.isActive = true
|
||||
|
||||
self.addSubview(self.subtitleLabel)
|
||||
self.subtitleLabel.textColor = UIColor.init(hex: "8E8E92")
|
||||
self.subtitleLabel.font = UIFont.system(weight: .demiBold, size: 13)
|
||||
self.subtitleLabel.textColor = UIColor.init(red: 142 / 255.0, green: 142 / 255.0, blue: 146 / 255.0, alpha: 1)
|
||||
self.subtitleLabel.font = UIFont.systemFont(ofSize: 13, weight: .semibold)
|
||||
self.subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.subtitleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 16).isActive = true
|
||||
self.subtitleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -16).isActive = true
|
||||
self.subtitleLabel.bottomAnchor.constraint(equalTo: self.titleLabel.topAnchor, constant: 0).isActive = true
|
||||
|
||||
self.leftButton.setTitleColor(self.elementsColor)
|
||||
self.leftButton.setTitleColor(self.elementsColor, for: .normal)
|
||||
self.leftButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
|
||||
self.leftButton.titleLabel?.textAlignment = .left
|
||||
self.leftButton.titleLabel?.font = UIFont.system(weight: .demiBold, size: 16)
|
||||
self.leftButton.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
|
||||
self.leftButton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 17, bottom: 0, right: 0)
|
||||
self.addSubview(self.leftButton)
|
||||
self.leftButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.leftButton.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
|
||||
self.leftButton.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -12).isActive = true
|
||||
|
||||
self.rightButton.setTitleColor(self.elementsColor)
|
||||
self.rightButton.setTitleColor(self.elementsColor, for: .normal)
|
||||
self.rightButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
|
||||
self.rightButton.titleLabel?.textAlignment = .right
|
||||
self.rightButton.titleLabel?.font = UIFont.system(weight: .demiBold, size: 17)
|
||||
self.rightButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
|
||||
self.rightButton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 16)
|
||||
self.addSubview(self.rightButton)
|
||||
self.rightButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
@@ -151,7 +159,7 @@ public class SPFakeBarView: UIView {
|
||||
self.updateStyle()
|
||||
}
|
||||
|
||||
public override func layoutSubviews() {
|
||||
override open func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.setContraints()
|
||||
}
|
||||
@@ -177,7 +185,7 @@ public class SPFakeBarView: UIView {
|
||||
private func updateStyle() {
|
||||
switch self.style {
|
||||
case .small:
|
||||
if UIViewController.statusBarHeight == 44 {
|
||||
if UIApplication.shared.statusBarFrame.height == 44 {
|
||||
self.height = 88 - 44
|
||||
self.titleBottomConstraint?.constant = -12
|
||||
} else {
|
||||
@@ -185,26 +193,29 @@ public class SPFakeBarView: UIView {
|
||||
self.titleBottomConstraint?.constant = -12
|
||||
}
|
||||
self.addStatusBarHeight = true
|
||||
self.titleLabel.font = UIFont.system(weight: .demiBold, size: 17)
|
||||
self.titleLabel.setCenterAlignment()
|
||||
self.titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
|
||||
self.titleLabel.textAlignment = .center
|
||||
case .stork:
|
||||
self.height = 66
|
||||
self.titleBottomConstraint?.constant = -12
|
||||
self.addStatusBarHeight = false
|
||||
self.titleLabel.font = UIFont.system(weight: .demiBold, size: 17)
|
||||
self.titleLabel.setCenterAlignment()
|
||||
self.titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
|
||||
self.titleLabel.textAlignment = .center
|
||||
case .large:
|
||||
if UIViewController.statusBarHeight == 44 {
|
||||
if UIApplication.shared.statusBarFrame.height == 44 {
|
||||
self.height = 140 - 44
|
||||
self.titleBottomConstraint?.constant = -8
|
||||
} else {
|
||||
self.height = 112 - 20
|
||||
self.height = 116 - 20
|
||||
self.titleBottomConstraint?.constant = -4
|
||||
}
|
||||
self.addStatusBarHeight = true
|
||||
self.titleLabel.font = UIFont.system(weight: .bold, size: 34)
|
||||
self.titleLabel.font = UIFont.systemFont(ofSize: 34, weight: .bold)
|
||||
self.titleLabel.textAlignment = .left
|
||||
break
|
||||
case .noContent:
|
||||
self.height = 0
|
||||
self.addStatusBarHeight = true
|
||||
}
|
||||
|
||||
self.updateConstraints()
|
||||
@@ -222,3 +233,18 @@ public class SPFakeBarView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
extension SPFakeBarView {
|
||||
|
||||
static var navigationElementsColor: UIColor {
|
||||
get {
|
||||
if UINavigationBar.appearance().tintColor != nil {
|
||||
return UINavigationBar.appearance().tintColor
|
||||
} else {
|
||||
return UIColor.init(red: 0 / 255.0, green: 122 / 255.0, blue: 255 / 255.0, alpha: 1)
|
||||
}
|
||||
}
|
||||
set {
|
||||
UINavigationBar.appearance().tintColor = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import UIKit
|
||||
|
||||
class SPStorkCodeDraw : NSObject {
|
||||
|
||||
private struct Cache {
|
||||
static let gradient: CGGradient = CGGradient(colorsSpace: nil, colors: [UIColor.red.cgColor, UIColor.red.cgColor] as CFArray, locations: [0, 1])!
|
||||
}
|
||||
|
||||
@objc dynamic class var gradient: CGGradient { return Cache.gradient }
|
||||
|
||||
@objc dynamic class func drawClose(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
context.saveGState()
|
||||
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 100, height: 100), target: targetFrame)
|
||||
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
|
||||
context.scaleBy(x: resizedFrame.width / 100, y: resizedFrame.height / 100)
|
||||
|
||||
let bezierPath = UIBezierPath()
|
||||
bezierPath.move(to: CGPoint(x: 92.02, y: 22.92))
|
||||
bezierPath.addLine(to: CGPoint(x: 64.42, y: 50.52))
|
||||
bezierPath.addLine(to: CGPoint(x: 92.02, y: 78.13))
|
||||
bezierPath.addCurve(to: CGPoint(x: 92.02, y: 92.99), controlPoint1: CGPoint(x: 96.13, y: 82.23), controlPoint2: CGPoint(x: 96.13, y: 88.89))
|
||||
bezierPath.addCurve(to: CGPoint(x: 84.59, y: 96.07), controlPoint1: CGPoint(x: 89.97, y: 95.05), controlPoint2: CGPoint(x: 87.28, y: 96.07))
|
||||
bezierPath.addCurve(to: CGPoint(x: 77.16, y: 92.99), controlPoint1: CGPoint(x: 81.9, y: 96.07), controlPoint2: CGPoint(x: 79.22, y: 95.05))
|
||||
bezierPath.addLine(to: CGPoint(x: 49.55, y: 65.38))
|
||||
bezierPath.addLine(to: CGPoint(x: 21.95, y: 92.99))
|
||||
bezierPath.addCurve(to: CGPoint(x: 14.51, y: 96.07), controlPoint1: CGPoint(x: 19.89, y: 95.05), controlPoint2: CGPoint(x: 17.2, y: 96.07))
|
||||
bezierPath.addCurve(to: CGPoint(x: 7.08, y: 92.99), controlPoint1: CGPoint(x: 11.82, y: 96.07), controlPoint2: CGPoint(x: 9.13, y: 95.05))
|
||||
bezierPath.addCurve(to: CGPoint(x: 7.08, y: 78.13), controlPoint1: CGPoint(x: 2.97, y: 88.89), controlPoint2: CGPoint(x: 2.97, y: 82.23))
|
||||
bezierPath.addLine(to: CGPoint(x: 34.69, y: 50.52))
|
||||
bezierPath.addLine(to: CGPoint(x: 7.08, y: 22.92))
|
||||
bezierPath.addCurve(to: CGPoint(x: 7.08, y: 8.04), controlPoint1: CGPoint(x: 2.97, y: 18.8), controlPoint2: CGPoint(x: 2.97, y: 12.15))
|
||||
bezierPath.addCurve(to: CGPoint(x: 21.94, y: 8.04), controlPoint1: CGPoint(x: 11.18, y: 3.94), controlPoint2: CGPoint(x: 17.84, y: 3.94))
|
||||
bezierPath.addLine(to: CGPoint(x: 49.55, y: 35.65))
|
||||
bezierPath.addLine(to: CGPoint(x: 77.16, y: 8.04))
|
||||
bezierPath.addCurve(to: CGPoint(x: 92.02, y: 8.04), controlPoint1: CGPoint(x: 81.26, y: 3.94), controlPoint2: CGPoint(x: 87.92, y: 3.94))
|
||||
bezierPath.addCurve(to: CGPoint(x: 92.02, y: 22.92), controlPoint1: CGPoint(x: 96.13, y: 12.15), controlPoint2: CGPoint(x: 96.13, y: 18.8))
|
||||
bezierPath.close()
|
||||
color.setFill()
|
||||
bezierPath.fill()
|
||||
|
||||
context.restoreGState()
|
||||
|
||||
}
|
||||
|
||||
@objc(StyleKitNameResizingBehavior)
|
||||
enum ResizingBehavior: Int {
|
||||
case aspectFit
|
||||
case aspectFill
|
||||
case stretch
|
||||
case center
|
||||
|
||||
func apply(rect: CGRect, target: CGRect) -> CGRect {
|
||||
if rect == target || target == CGRect.zero {
|
||||
return rect
|
||||
}
|
||||
|
||||
var scales = CGSize.zero
|
||||
scales.width = abs(target.width / rect.width)
|
||||
scales.height = abs(target.height / rect.height)
|
||||
|
||||
switch self {
|
||||
case .aspectFit:
|
||||
scales.width = min(scales.width, scales.height)
|
||||
scales.height = scales.width
|
||||
case .aspectFill:
|
||||
scales.width = max(scales.width, scales.height)
|
||||
scales.height = scales.width
|
||||
case .stretch:
|
||||
break
|
||||
case .center:
|
||||
scales.width = 1
|
||||
scales.height = 1
|
||||
}
|
||||
|
||||
var result = rect.standardized
|
||||
result.size.width *= scales.width
|
||||
result.size.height *= scales.height
|
||||
result.origin.x = target.minX + (target.width - result.width) / 2
|
||||
result.origin.y = target.minY + (target.height - result.height) / 2
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
private override init() {}
|
||||
}
|
||||
@@ -29,9 +29,19 @@ extension UIViewController {
|
||||
&& presentingViewController != nil
|
||||
}
|
||||
|
||||
public func presentAsStork(_ controller: UIViewController, height: CGFloat? = nil, showIndicator: Bool = false, complection: (() -> Void)? = nil) {
|
||||
public func presentAsStork(_ controller: UIViewController, height: CGFloat? = nil) {
|
||||
let transitionDelegate = SPStorkTransitioningDelegate()
|
||||
transitionDelegate.customHeight = height
|
||||
controller.transitioningDelegate = transitionDelegate
|
||||
controller.modalPresentationStyle = .custom
|
||||
controller.modalPresentationCapturesStatusBarAppearance = true
|
||||
self.present(controller, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
public func presentAsStork(_ controller: UIViewController, height: CGFloat?, showIndicator: Bool, showCloseButton: Bool, complection: (() -> Void)? = nil) {
|
||||
let transitionDelegate = SPStorkTransitioningDelegate()
|
||||
transitionDelegate.customHeight = height
|
||||
transitionDelegate.showCloseButton = showCloseButton
|
||||
transitionDelegate.showIndicator = showIndicator
|
||||
controller.transitioningDelegate = transitionDelegate
|
||||
controller.modalPresentationStyle = .custom
|
||||
@@ -0,0 +1,29 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public enum SPStorkArrowMode {
|
||||
|
||||
case auto
|
||||
case alwaysArrow
|
||||
case alwaysLine
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public enum SPStorkHapticMoments {
|
||||
|
||||
case willPresent
|
||||
case willDismiss
|
||||
case willDismissIfRelease
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
@objc public protocol SPStorkControllerConfirmDelegate: class {
|
||||
|
||||
var needConfirm: Bool { get }
|
||||
|
||||
func confirm(_ completion: @escaping (_ isConfirmed: Bool)->())
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
@objc public protocol SPStorkControllerDelegate: class {
|
||||
|
||||
@objc optional func didDismissStorkBySwipe()
|
||||
|
||||
@objc optional func didDismissStorkByTap()
|
||||
}
|
||||
@@ -21,11 +21,11 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public struct SPStorkController {
|
||||
public enum SPStorkController {
|
||||
|
||||
static public func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if let controller = self.controller(for: scrollView) {
|
||||
if let presentationController = controller.presentationController as? SPStorkPresentationController {
|
||||
if let presentationController = self.presentationController(for: controller) {
|
||||
let translation = -(scrollView.contentOffset.y + scrollView.contentInset.top)
|
||||
if translation >= 0 {
|
||||
if controller.isBeingPresented { return }
|
||||
@@ -35,7 +35,9 @@ public struct SPStorkController {
|
||||
presentationController.setIndicator(style: scrollView.isTracking ? .line : .arrow)
|
||||
if translation >= presentationController.translateForDismiss * 0.4 {
|
||||
if !scrollView.isTracking && !scrollView.isDragging {
|
||||
presentationController.presentedViewController.dismiss(animated: true, completion: nil)
|
||||
self.dismissWithConfirmation(controller: controller, completion: {
|
||||
presentationController.storkDelegate?.didDismissStorkBySwipe?()
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -46,11 +48,25 @@ public struct SPStorkController {
|
||||
presentationController.setIndicator(style: .arrow)
|
||||
presentationController.scrollViewDidScroll(0)
|
||||
}
|
||||
|
||||
if translation < -5 {
|
||||
presentationController.setIndicator(visible: false, forse: (translation < -50))
|
||||
} else {
|
||||
presentationController.setIndicator(visible: true, forse: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static var topScrollIndicatorInset: CGFloat {
|
||||
static public func dismissWithConfirmation(controller: UIViewController, completion: (()->())?) {
|
||||
if let controller = self.presentationController(for: controller) {
|
||||
controller.dismissWithConfirmation(prepare: nil, completion: {
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static public var topScrollIndicatorInset: CGFloat {
|
||||
return 6
|
||||
}
|
||||
|
||||
@@ -66,6 +82,19 @@ public struct SPStorkController {
|
||||
}
|
||||
}
|
||||
|
||||
static private func presentationController(for controller: UIViewController) -> SPStorkPresentationController? {
|
||||
guard controller.modalPresentationStyle == .custom else { return nil }
|
||||
|
||||
if let presentationController = controller.presentationController as? SPStorkPresentationController {
|
||||
return presentationController
|
||||
}
|
||||
|
||||
if let presentationController = controller.parent?.presentationController as? SPStorkPresentationController {
|
||||
return presentationController
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
static private func controller(for view: UIView) -> UIViewController? {
|
||||
var nextResponder = view.next
|
||||
while nextResponder != nil && !(nextResponder! is UIViewController) {
|
||||
@@ -73,6 +102,4 @@ public struct SPStorkController {
|
||||
}
|
||||
return nextResponder as? UIViewController
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPStorkSegue: UIStoryboardSegue {
|
||||
|
||||
public var transitioningDelegate: SPStorkTransitioningDelegate?
|
||||
|
||||
override public func perform() {
|
||||
transitioningDelegate = transitioningDelegate ?? SPStorkTransitioningDelegate()
|
||||
destination.transitioningDelegate = transitioningDelegate
|
||||
destination.modalPresentationStyle = .custom
|
||||
super.perform()
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,10 @@ final class SPStorkDismissingAnimationController: NSObject, UIViewControllerAnim
|
||||
return
|
||||
}
|
||||
|
||||
let finalFrameForPresentedView = transitionContext.finalFrame(for: presentedViewController)
|
||||
|
||||
let containerView = transitionContext.containerView
|
||||
let offscreenFrame = CGRect(x: 0, y: containerView.bounds.height, width: containerView.bounds.width, height: containerView.bounds.height)
|
||||
let offscreenFrame = CGRect(x: 0, y: containerView.bounds.height, width: finalFrameForPresentedView.width, height: finalFrameForPresentedView.height)
|
||||
|
||||
UIView.animate(
|
||||
withDuration: transitionDuration(using: transitionContext),
|
||||
@@ -25,16 +25,23 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
|
||||
var swipeToDismissEnabled: Bool = true
|
||||
var tapAroundToDismissEnabled: Bool = true
|
||||
var showCloseButton: Bool = false
|
||||
var showIndicator: Bool = true
|
||||
var indicatorColor: UIColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
|
||||
var hideIndicatorWhenScroll: Bool = false
|
||||
var indicatorMode: SPStorkArrowMode = .auto
|
||||
var customHeight: CGFloat? = nil
|
||||
var translateForDismiss: CGFloat = 200
|
||||
var hapticMoments: [SPStorkHapticMoments] = [.willDismissIfRelease]
|
||||
|
||||
var transitioningDelegate: SPStorkTransitioningDelegate?
|
||||
weak var storkDelegate: SPStorkControllerDelegate?
|
||||
weak var confirmDelegate: SPStorkControllerConfirmDelegate?
|
||||
|
||||
var pan: UIPanGestureRecognizer?
|
||||
var tap: UITapGestureRecognizer?
|
||||
|
||||
private var closeButton = SPStorkCloseButton()
|
||||
private var indicatorView = SPStorkIndicatorView()
|
||||
private var gradeView: UIView = UIView()
|
||||
private let snapshotViewContainer = UIView()
|
||||
@@ -45,8 +52,10 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
private var snapshotViewWidthConstraint: NSLayoutConstraint?
|
||||
private var snapshotViewAspectRatioConstraint: NSLayoutConstraint?
|
||||
|
||||
var workConfirmation: Bool = false
|
||||
private var workGester: Bool = false
|
||||
private var startDismissing: Bool = false
|
||||
private var afterReleaseDismissing: Bool = false
|
||||
|
||||
private var topSpace: CGFloat {
|
||||
let statusBarHeight: CGFloat = UIApplication.shared.statusBarFrame.height
|
||||
@@ -58,38 +67,85 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
|
||||
private var scaleForPresentingView: CGFloat {
|
||||
guard let containerView = containerView else { return 0 }
|
||||
let factor = 1 - (self.topSpace * 2 / containerView.frame.height)
|
||||
let factor = 1 - ((self.cornerRadius + 3) * 2 / containerView.frame.width)
|
||||
return factor
|
||||
}
|
||||
|
||||
private var feedbackGenerator = UIImpactFeedbackGenerator(style: .light)
|
||||
|
||||
override var presentedView: UIView? {
|
||||
let view = self.presentedViewController.view
|
||||
if view?.frame.origin == CGPoint.zero {
|
||||
view?.frame = self.frameOfPresentedViewInContainerView
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
override var frameOfPresentedViewInContainerView: CGRect {
|
||||
guard let containerView = containerView else { return .zero }
|
||||
let baseY: CGFloat = self.topSpace + 13
|
||||
let maxHeight: CGFloat = containerView.bounds.height - baseY
|
||||
var height: CGFloat = maxHeight
|
||||
|
||||
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")
|
||||
if let customHeight = self.customHeight {
|
||||
if customHeight < maxHeight {
|
||||
height = customHeight
|
||||
} else {
|
||||
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)
|
||||
return CGRect(x: 0, y: containerView.bounds.height - height, width: containerView.bounds.width, height: height)
|
||||
}
|
||||
|
||||
override func presentationTransitionWillBegin() {
|
||||
super.presentationTransitionWillBegin()
|
||||
|
||||
if !self.hapticMoments.isEmpty {
|
||||
self.feedbackGenerator.prepare()
|
||||
}
|
||||
|
||||
guard let containerView = self.containerView, let presentedView = self.presentedView, let window = containerView.window else { return }
|
||||
|
||||
let closeTitle = NSLocalizedString("Close", comment: "Close")
|
||||
|
||||
if self.showIndicator {
|
||||
self.indicatorView.color = self.indicatorColor
|
||||
let tap = UITapGestureRecognizer.init(target: self, action: #selector(self.dismissAction))
|
||||
let tap = UITapGestureRecognizer.init(target: self, action: #selector(self.tapIndicator))
|
||||
tap.cancelsTouchesInView = false
|
||||
self.indicatorView.addGestureRecognizer(tap)
|
||||
self.indicatorView.accessibilityLabel = closeTitle
|
||||
presentedView.addSubview(self.indicatorView)
|
||||
self.indicatorView.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.indicatorView.widthAnchor.constraint(equalToConstant: 36).isActive = true
|
||||
self.indicatorView.heightAnchor.constraint(equalToConstant: 13).isActive = true
|
||||
self.indicatorView.centerXAnchor.constraint(equalTo: presentedView.centerXAnchor).isActive = true
|
||||
self.indicatorView.topAnchor.constraint(equalTo: presentedView.topAnchor, constant: 12).isActive = true
|
||||
self.indicatorView.mode = self.indicatorMode
|
||||
|
||||
if UIAccessibility.isVoiceOverRunning {
|
||||
let accessibleIndicatorOverlayButton = UIButton(type: .custom)
|
||||
accessibleIndicatorOverlayButton.addTarget(self, action: #selector(self.tapIndicator), for: .touchUpInside)
|
||||
accessibleIndicatorOverlayButton.accessibilityLabel = closeTitle
|
||||
presentedView.addSubview(accessibleIndicatorOverlayButton)
|
||||
accessibleIndicatorOverlayButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
accessibleIndicatorOverlayButton.leadingAnchor.constraint(equalTo: presentedView.leadingAnchor),
|
||||
accessibleIndicatorOverlayButton.trailingAnchor.constraint(equalTo: presentedView.trailingAnchor),
|
||||
accessibleIndicatorOverlayButton.topAnchor.constraint(equalTo: presentedView.topAnchor),
|
||||
accessibleIndicatorOverlayButton.bottomAnchor.constraint(equalTo: self.indicatorView.bottomAnchor),
|
||||
])
|
||||
}
|
||||
}
|
||||
self.updateLayoutIndicator()
|
||||
self.indicatorView.style = .arrow
|
||||
self.gradeView.alpha = 0
|
||||
|
||||
self.closeButton.accessibilityLabel = closeTitle
|
||||
if self.showCloseButton {
|
||||
self.closeButton.addTarget(self, action: #selector(self.tapCloseButton), for: .touchUpInside)
|
||||
presentedView.addSubview(self.closeButton)
|
||||
}
|
||||
self.updateLayoutCloseButton()
|
||||
|
||||
let initialFrame: CGRect = presentingViewController.isPresentedAsStork ? presentingViewController.view.frame : containerView.bounds
|
||||
|
||||
@@ -156,6 +212,10 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
rootSnapshotView?.removeFromSuperview()
|
||||
rootSnapshotRoundedView?.removeFromSuperview()
|
||||
})
|
||||
|
||||
if self.hapticMoments.contains(.willPresent) {
|
||||
self.feedbackGenerator.impactOccurred()
|
||||
}
|
||||
}
|
||||
|
||||
override func presentationTransitionDidEnd(_ completed: Bool) {
|
||||
@@ -169,7 +229,7 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
self.updateSnapshotAspectRatio()
|
||||
|
||||
if self.tapAroundToDismissEnabled {
|
||||
self.tap = UITapGestureRecognizer.init(target: self, action: #selector(self.dismissAction))
|
||||
self.tap = UITapGestureRecognizer.init(target: self, action: #selector(self.tapArround))
|
||||
self.tap?.cancelsTouchesInView = false
|
||||
self.snapshotViewContainer.addGestureRecognizer(self.tap!)
|
||||
}
|
||||
@@ -183,19 +243,13 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
}
|
||||
}
|
||||
|
||||
@objc func dismissAction() {
|
||||
self.presentingViewController.view.endEditing(true)
|
||||
self.presentedViewController.view.endEditing(true)
|
||||
self.presentedViewController.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
override func dismissalTransitionWillBegin() {
|
||||
super.dismissalTransitionWillBegin()
|
||||
guard let containerView = containerView else { return }
|
||||
self.startDismissing = true
|
||||
|
||||
let initialFrame: CGRect = presentingViewController.isPresentedAsStork ? presentingViewController.view.frame : containerView.bounds
|
||||
|
||||
|
||||
let initialTransform = CGAffineTransform.identity
|
||||
.translatedBy(x: 0, y: -initialFrame.origin.y)
|
||||
.translatedBy(x: 0, y: self.topSpace)
|
||||
@@ -245,6 +299,9 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
self.snapshotView?.transform = .identity
|
||||
self.snapshotViewContainer.transform = finalTransform
|
||||
self.gradeView.alpha = 0
|
||||
if self.hapticMoments.contains(.willDismiss) {
|
||||
self.feedbackGenerator.impactOccurred()
|
||||
}
|
||||
}, completion: { _ in
|
||||
rootSnapshotView?.removeFromSuperview()
|
||||
rootSnapshotRoundedView?.removeFromSuperview()
|
||||
@@ -258,6 +315,8 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
self.backgroundView.removeFromSuperview()
|
||||
self.snapshotView?.removeFromSuperview()
|
||||
self.snapshotViewContainer.removeFromSuperview()
|
||||
self.indicatorView.removeFromSuperview()
|
||||
self.closeButton.removeFromSuperview()
|
||||
|
||||
let offscreenFrame = CGRect(x: 0, y: containerView.bounds.height, width: containerView.bounds.width, height: containerView.bounds.height)
|
||||
presentedViewController.view.frame = offscreenFrame
|
||||
@@ -267,6 +326,56 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
|
||||
extension SPStorkPresentationController {
|
||||
|
||||
@objc func tapIndicator() {
|
||||
self.dismissWithConfirmation(prepare: nil, completion: {
|
||||
self.storkDelegate?.didDismissStorkByTap?()
|
||||
})
|
||||
}
|
||||
|
||||
@objc func tapArround() {
|
||||
self.dismissWithConfirmation(prepare: nil, completion: {
|
||||
self.storkDelegate?.didDismissStorkByTap?()
|
||||
})
|
||||
}
|
||||
|
||||
@objc func tapCloseButton() {
|
||||
self.dismissWithConfirmation(prepare: nil, completion: {
|
||||
self.storkDelegate?.didDismissStorkByTap?()
|
||||
})
|
||||
}
|
||||
|
||||
public func dismissWithConfirmation(prepare: (()->())?, completion: (()->())?) {
|
||||
|
||||
let dismiss = {
|
||||
self.presentingViewController.view.endEditing(true)
|
||||
self.presentedViewController.view.endEditing(true)
|
||||
self.presentedViewController.dismiss(animated: true, completion: {
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
|
||||
guard let confirmDelegate = self.confirmDelegate else {
|
||||
dismiss()
|
||||
return
|
||||
}
|
||||
|
||||
if self.workConfirmation { return }
|
||||
|
||||
if confirmDelegate.needConfirm {
|
||||
prepare?()
|
||||
self.workConfirmation = true
|
||||
confirmDelegate.confirm({ (isConfirmed) in
|
||||
self.workConfirmation = false
|
||||
self.afterReleaseDismissing = false
|
||||
if isConfirmed {
|
||||
dismiss()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func handlePan(gestureRecognizer: UIPanGestureRecognizer) {
|
||||
guard gestureRecognizer.isEqual(self.pan), self.swipeToDismissEnabled else { return }
|
||||
|
||||
@@ -280,8 +389,8 @@ extension SPStorkPresentationController {
|
||||
gestureRecognizer.setTranslation(CGPoint(x: 0, y: 0), in: containerView)
|
||||
case .changed:
|
||||
self.workGester = true
|
||||
let translation = gestureRecognizer.translation(in: presentedView)
|
||||
if self.swipeToDismissEnabled {
|
||||
let translation = gestureRecognizer.translation(in: presentedView)
|
||||
self.updatePresentedViewForTranslation(inVerticalDirection: translation.y)
|
||||
} else {
|
||||
gestureRecognizer.setTranslation(.zero, in: presentedView)
|
||||
@@ -289,9 +398,8 @@ extension SPStorkPresentationController {
|
||||
case .ended:
|
||||
self.workGester = false
|
||||
let translation = gestureRecognizer.translation(in: presentedView).y
|
||||
if translation >= self.translateForDismiss {
|
||||
self.presentedViewController.dismiss(animated: true, completion: nil)
|
||||
} else {
|
||||
|
||||
let toDefault = {
|
||||
self.indicatorView.style = .arrow
|
||||
UIView.animate(
|
||||
withDuration: 0.6,
|
||||
@@ -305,11 +413,27 @@ extension SPStorkPresentationController {
|
||||
self.gradeView.alpha = self.alpha
|
||||
})
|
||||
}
|
||||
|
||||
if translation >= self.translateForDismiss {
|
||||
self.dismissWithConfirmation(prepare: toDefault, completion: {
|
||||
self.storkDelegate?.didDismissStorkBySwipe?()
|
||||
})
|
||||
} else {
|
||||
toDefault()
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if let gester = gestureRecognizer as? UIPanGestureRecognizer {
|
||||
let velocity = gester.velocity(in: self.presentedViewController.view)
|
||||
return abs(velocity.y) > abs(velocity.x)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ translation: CGFloat) {
|
||||
if !self.workGester {
|
||||
self.updatePresentedViewForTranslation(inVerticalDirection: translation)
|
||||
@@ -325,12 +449,28 @@ extension SPStorkPresentationController {
|
||||
self.indicatorView.style = style
|
||||
}
|
||||
|
||||
func setIndicator(visible: Bool, forse: Bool) {
|
||||
guard self.hideIndicatorWhenScroll else { return }
|
||||
let newAlpha: CGFloat = visible ? 1 : 0
|
||||
if forse {
|
||||
self.indicatorView.layer.removeAllAnimations()
|
||||
self.indicatorView.alpha = newAlpha
|
||||
return
|
||||
}
|
||||
if self.indicatorView.alpha == newAlpha {
|
||||
return
|
||||
}
|
||||
UIView.animate(withDuration: 0.18, animations: {
|
||||
self.indicatorView.alpha = newAlpha
|
||||
})
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -351,6 +491,18 @@ extension SPStorkPresentationController {
|
||||
} else {
|
||||
self.presentedView?.transform = CGAffineTransform.identity
|
||||
}
|
||||
|
||||
if self.swipeToDismissEnabled {
|
||||
let afterRealseDismissing = (translation >= self.translateForDismiss)
|
||||
if afterRealseDismissing != self.afterReleaseDismissing {
|
||||
self.afterReleaseDismissing = afterRealseDismissing
|
||||
if !self.workConfirmation {
|
||||
if self.hapticMoments.contains(.willDismissIfRelease) {
|
||||
self.feedbackGenerator.impactOccurred()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,10 +513,7 @@ extension SPStorkPresentationController {
|
||||
guard let containerView = containerView else { return }
|
||||
self.updateSnapshotAspectRatio()
|
||||
if presentedViewController.view.isDescendant(of: containerView) {
|
||||
UIView.animate(withDuration: 0.1) { [weak self] in
|
||||
guard let `self` = self else { return }
|
||||
self.presentedViewController.view.frame = self.frameOfPresentedViewInContainerView
|
||||
}
|
||||
self.presentedViewController.view.frame = self.frameOfPresentedViewInContainerView
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,6 +521,7 @@ extension SPStorkPresentationController {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
coordinator.animate(alongsideTransition: { contex in
|
||||
self.updateLayoutIndicator()
|
||||
self.updateLayoutCloseButton()
|
||||
}, completion: { [weak self] _ in
|
||||
self?.updateSnapshotAspectRatio()
|
||||
self?.updateSnapshot()
|
||||
@@ -379,11 +529,15 @@ extension SPStorkPresentationController {
|
||||
}
|
||||
|
||||
private func updateLayoutIndicator() {
|
||||
guard let presentedView = self.presentedView else { return }
|
||||
self.indicatorView.style = .line
|
||||
self.indicatorView.sizeToFit()
|
||||
self.indicatorView.frame.origin.y = 12
|
||||
self.indicatorView.center.x = presentedView.frame.width / 2
|
||||
self.indicatorView.style = .arrow
|
||||
}
|
||||
|
||||
private func updateLayoutCloseButton() {
|
||||
guard let presentedView = self.presentedView else { return }
|
||||
self.closeButton.sizeToFit()
|
||||
self.closeButton.layout(bottomX: presentedView.frame.width - 19, y: 19)
|
||||
}
|
||||
|
||||
private func updateSnapshot() {
|
||||
@@ -25,22 +25,34 @@ public final class SPStorkTransitioningDelegate: NSObject, UIViewControllerTrans
|
||||
|
||||
public var swipeToDismissEnabled: Bool = true
|
||||
public var tapAroundToDismissEnabled: Bool = true
|
||||
public var showCloseButton: Bool = false
|
||||
public var showIndicator: Bool = true
|
||||
public var indicatorColor: UIColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
|
||||
public var hideIndicatorWhenScroll: Bool = false
|
||||
public var indicatorMode: SPStorkArrowMode = .auto
|
||||
public var customHeight: CGFloat? = nil
|
||||
public var translateForDismiss: CGFloat = 200
|
||||
public var cornerRadius: CGFloat = 10
|
||||
public var hapticMoments: [SPStorkHapticMoments] = [.willDismissIfRelease]
|
||||
public weak var storkDelegate: SPStorkControllerDelegate? = nil
|
||||
public weak var confirmDelegate: SPStorkControllerConfirmDelegate? = nil
|
||||
|
||||
public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
|
||||
let controller = SPStorkPresentationController(presentedViewController: presented, presenting: presenting)
|
||||
controller.swipeToDismissEnabled = self.swipeToDismissEnabled
|
||||
controller.tapAroundToDismissEnabled = self.tapAroundToDismissEnabled
|
||||
controller.showCloseButton = self.showCloseButton
|
||||
controller.showIndicator = self.showIndicator
|
||||
controller.indicatorColor = self.indicatorColor
|
||||
controller.hideIndicatorWhenScroll = self.hideIndicatorWhenScroll
|
||||
controller.indicatorMode = self.indicatorMode
|
||||
controller.customHeight = self.customHeight
|
||||
controller.translateForDismiss = self.translateForDismiss
|
||||
controller.cornerRadius = self.cornerRadius
|
||||
controller.hapticMoments = self.hapticMoments
|
||||
controller.transitioningDelegate = self
|
||||
controller.storkDelegate = self.storkDelegate
|
||||
controller.confirmDelegate = self.confirmDelegate
|
||||
return controller
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
open class SPStorkCloseButton: UIButton {
|
||||
|
||||
let iconView = SPStorkCloseView()
|
||||
|
||||
var widthIconFactor: CGFloat = 1
|
||||
var heightIconFactor: CGFloat = 1
|
||||
|
||||
var color = UIColor.blue {
|
||||
didSet {
|
||||
self.iconView.color = self.color
|
||||
}
|
||||
}
|
||||
|
||||
override open var isHighlighted: Bool {
|
||||
didSet {
|
||||
self.iconView.color = self.color.withAlphaComponent(self.isHighlighted ? 0.7 : 1)
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
super.init(frame: .zero)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
self.iconView.isUserInteractionEnabled = false
|
||||
self.addSubview(self.iconView)
|
||||
self.backgroundColor = UIColor.init(red: 239 / 255.0, green: 239 / 255.0, blue: 244 / 255.0, alpha: 1)
|
||||
self.color = UIColor.init(red: 142 / 255.0, green: 142 / 255.0, blue: 147 / 255.0, alpha: 1)
|
||||
self.widthIconFactor = 0.36
|
||||
self.heightIconFactor = 0.36
|
||||
}
|
||||
|
||||
func layout(bottomX: CGFloat, y: CGFloat) {
|
||||
self.sizeToFit()
|
||||
self.frame.origin.x = bottomX - self.frame.width
|
||||
self.frame.origin.y = y
|
||||
}
|
||||
|
||||
override open func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.layer.cornerRadius = self.frame.width / 2
|
||||
self.iconView.frame = CGRect.init(x: 0, y: 0, width: self.frame.width * self.widthIconFactor, height: self.frame.height * self.heightIconFactor)
|
||||
self.iconView.center = CGPoint.init(x: self.frame.width / 2, y: self.frame.height / 2)
|
||||
}
|
||||
|
||||
override open func sizeToFit() {
|
||||
super.sizeToFit()
|
||||
self.frame = CGRect.init(x: self.frame.origin.x, y: self.frame.origin.y, width: 30, height: 30)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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
|
||||
|
||||
open class SPStorkCloseView: UIView {
|
||||
|
||||
var color = UIColor.blue {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
super.init(frame: CGRect.zero)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
self.backgroundColor = UIColor.clear
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override open func draw(_ rect: CGRect) {
|
||||
super.draw(rect)
|
||||
SPStorkCodeDraw.drawClose(frame: rect, resizing: .aspectFit, color: self.color)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,27 +21,45 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPStorkIndicatorView: UIView {
|
||||
open class SPStorkIndicatorView: UIView {
|
||||
|
||||
var style: Style = .line {
|
||||
didSet {
|
||||
switch self.style {
|
||||
case .line:
|
||||
self.animate {
|
||||
self.leftView.transform = .identity
|
||||
self.rightView.transform = .identity
|
||||
}
|
||||
case .arrow:
|
||||
self.animate {
|
||||
let angle = CGFloat(20 * Float.pi / 180)
|
||||
self.leftView.transform = CGAffineTransform.init(rotationAngle: angle)
|
||||
self.rightView.transform = CGAffineTransform.init(rotationAngle: -angle)
|
||||
if self.mode == .auto {
|
||||
switch self.style {
|
||||
case .line:
|
||||
self.animate {
|
||||
self.leftView.transform = .identity
|
||||
self.rightView.transform = .identity
|
||||
}
|
||||
case .arrow:
|
||||
self.animate {
|
||||
let angle = CGFloat(20 * Float.pi / 180)
|
||||
self.leftView.transform = CGAffineTransform.init(rotationAngle: angle)
|
||||
self.rightView.transform = CGAffineTransform.init(rotationAngle: -angle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.mode == .alwaysArrow {
|
||||
self.leftView.removeAllAnimations()
|
||||
self.rightView.removeAllAnimations()
|
||||
self.leftView.transform = .identity
|
||||
self.rightView.transform = .identity
|
||||
let angle = CGFloat(20 * Float.pi / 180)
|
||||
self.leftView.transform = CGAffineTransform.init(rotationAngle: angle)
|
||||
self.rightView.transform = CGAffineTransform.init(rotationAngle: -angle)
|
||||
}
|
||||
|
||||
if self.mode == .alwaysLine {
|
||||
self.leftView.transform = .identity
|
||||
self.rightView.transform = .identity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var mode: SPStorkArrowMode = .auto
|
||||
|
||||
var color: UIColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1) {
|
||||
didSet {
|
||||
self.leftView.backgroundColor = self.color
|
||||
@@ -60,12 +78,16 @@ class SPStorkIndicatorView: UIView {
|
||||
self.color = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func sizeToFit() {
|
||||
override open func sizeToFit() {
|
||||
super.sizeToFit()
|
||||
|
||||
self.leftView.transform = .identity
|
||||
self.rightView.transform = .identity
|
||||
|
||||
self.frame = CGRect.init(x: self.frame.origin.x, y: self.frame.origin.y, width: 36, height: 13)
|
||||
|
||||
let height: CGFloat = 5
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPAnimation {
|
||||
enum SPAnimation {
|
||||
|
||||
public static func animate(_ duration: TimeInterval,
|
||||
static func animate(_ duration: TimeInterval,
|
||||
animations: (() -> Void)!,
|
||||
delay: TimeInterval = 0,
|
||||
options: UIView.AnimationOptions = [],
|
||||
@@ -40,7 +40,7 @@ public class SPAnimation {
|
||||
})
|
||||
}
|
||||
|
||||
public static func animateWithRepeatition(_ duration: TimeInterval,
|
||||
static func animateWithRepeatition(_ duration: TimeInterval,
|
||||
animations: (() -> Void)!,
|
||||
delay: TimeInterval = 0,
|
||||
options: UIView.AnimationOptions = [],
|
||||
|
||||
@@ -21,13 +21,13 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPAnimationAlpha {
|
||||
class SPAnimationAlpha {
|
||||
|
||||
public static let durationListAnimation: TimeInterval = 0.45
|
||||
public static let coefLenthForTransition: CGFloat = 2.8
|
||||
public static let delayPerItem: TimeInterval = 0.09
|
||||
static let durationListAnimation: TimeInterval = 0.45
|
||||
static let coefLenthForTransition: CGFloat = 2.8
|
||||
static let delayPerItem: TimeInterval = 0.09
|
||||
|
||||
public static func hideList(_ duration: TimeInterval = durationListAnimation,
|
||||
static func hideList(_ duration: TimeInterval = durationListAnimation,
|
||||
views: [UIView],
|
||||
delayPerItem: TimeInterval = delayPerItem,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
@@ -51,7 +51,7 @@ public class SPAnimationAlpha {
|
||||
}
|
||||
}
|
||||
|
||||
public static func hideReverseList(_ duration: TimeInterval = durationListAnimation,
|
||||
static func hideReverseList(_ duration: TimeInterval = durationListAnimation,
|
||||
views: [UIView],
|
||||
delayPerItem: TimeInterval = delayPerItem,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
@@ -76,7 +76,7 @@ public class SPAnimationAlpha {
|
||||
}
|
||||
}
|
||||
|
||||
public static func showList(_ duration: TimeInterval = durationListAnimation,
|
||||
static func showList(_ duration: TimeInterval = durationListAnimation,
|
||||
views: [UIView],
|
||||
delayPerItem: TimeInterval = delayPerItem,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
|
||||
@@ -21,12 +21,12 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPAnimationSpring {
|
||||
class SPAnimationSpring {
|
||||
|
||||
public static let spring: CGFloat = 1
|
||||
public static let velocity: CGFloat = 1
|
||||
static let spring: CGFloat = 1
|
||||
static let velocity: CGFloat = 1
|
||||
|
||||
public static func animate(_ duration: TimeInterval,
|
||||
static func animate(_ duration: TimeInterval,
|
||||
animations: (() -> Void)!,
|
||||
delay: TimeInterval = 0,
|
||||
spring: CGFloat = spring,
|
||||
@@ -47,7 +47,7 @@ public class SPAnimationSpring {
|
||||
})
|
||||
}
|
||||
|
||||
public static func animateWithRepeatition(_ duration: TimeInterval,
|
||||
static func animateWithRepeatition(_ duration: TimeInterval,
|
||||
animations: (() -> Void)!,
|
||||
delay: TimeInterval = 0,
|
||||
spring: CGFloat = spring,
|
||||
|
||||
@@ -21,13 +21,13 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPAnimationUpward {
|
||||
class SPAnimationUpward {
|
||||
|
||||
public static let durationListAnimation: TimeInterval = 0.45
|
||||
public static let coefLenthForTransition: CGFloat = 2.8
|
||||
public static let delayPerItem: TimeInterval = 0.09
|
||||
static let durationListAnimation: TimeInterval = 0.45
|
||||
static let coefLenthForTransition: CGFloat = 2.8
|
||||
static let delayPerItem: TimeInterval = 0.09
|
||||
|
||||
public static func hide(_ duration: TimeInterval,
|
||||
static func hide(_ duration: TimeInterval,
|
||||
view: UIView,
|
||||
delay: TimeInterval = 0,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
@@ -48,7 +48,7 @@ public class SPAnimationUpward {
|
||||
})
|
||||
}
|
||||
|
||||
public static func hideList(_ duration: TimeInterval = durationListAnimation,
|
||||
static func hideList(_ duration: TimeInterval = durationListAnimation,
|
||||
views: [UIView],
|
||||
delayPerItem: TimeInterval = delayPerItem,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
@@ -69,7 +69,7 @@ public class SPAnimationUpward {
|
||||
|
||||
}
|
||||
|
||||
public static func show(_ duration: TimeInterval,
|
||||
static func show(_ duration: TimeInterval,
|
||||
view: UIView,
|
||||
delay: TimeInterval = 0,
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
@@ -93,7 +93,7 @@ public class SPAnimationUpward {
|
||||
})
|
||||
}
|
||||
|
||||
public static func showList(_ duration: TimeInterval = durationListAnimation,
|
||||
static func showList(_ duration: TimeInterval = durationListAnimation,
|
||||
views: [UIView],
|
||||
delayPerItem: TimeInterval = delayPerItem,
|
||||
options: UIView.AnimationOptions = [],
|
||||
|
||||
@@ -20,23 +20,22 @@
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
import StoreKit
|
||||
|
||||
struct SPApp {
|
||||
enum SPApp {
|
||||
|
||||
public static var udid: String? {
|
||||
static var udid: String? {
|
||||
return UIDevice.current.identifierForVendor?.uuidString
|
||||
}
|
||||
|
||||
public static var displayName: String? {
|
||||
static var displayName: String? {
|
||||
return Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
|
||||
}
|
||||
|
||||
public static var rootController: UIViewController? {
|
||||
static var rootController: UIViewController? {
|
||||
return UIApplication.shared.keyWindow?.rootViewController
|
||||
}
|
||||
|
||||
public static var safeArea: UIEdgeInsets {
|
||||
static var safeArea: UIEdgeInsets {
|
||||
if #available(iOS 11.0, *) {
|
||||
return UIApplication.shared.keyWindow?.safeArea ?? UIEdgeInsets.zero
|
||||
} else {
|
||||
@@ -44,7 +43,7 @@ struct SPApp {
|
||||
}
|
||||
}
|
||||
|
||||
public static func set(rootController: UIViewController, animatable: Bool = true) {
|
||||
static func set(rootController: UIViewController, animatable: Bool = true) {
|
||||
|
||||
rootController.view.frame = UIScreen.main.bounds
|
||||
|
||||
@@ -65,12 +64,10 @@ struct SPApp {
|
||||
}
|
||||
}
|
||||
|
||||
public static func set(elementsColor: UIColor) {
|
||||
static func set(elementsColor: UIColor) {
|
||||
UINavigationController.elementsColor = elementsColor
|
||||
UIAlertController.elementsColor = elementsColor
|
||||
UITabBarController.elementsColor = elementsColor
|
||||
UITabBar.appearance().tintColor = elementsColor
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ import UIKit
|
||||
|
||||
extension SPApp {
|
||||
|
||||
public struct Badge {
|
||||
struct Badge {
|
||||
|
||||
public static var number: Int {
|
||||
static var number: Int {
|
||||
get {
|
||||
return UIApplication.shared.applicationIconBadgeNumber
|
||||
}
|
||||
@@ -34,7 +34,7 @@ extension SPApp {
|
||||
}
|
||||
}
|
||||
|
||||
public static func reset() {
|
||||
static func reset() {
|
||||
UIApplication.shared.applicationIconBadgeNumber = 0
|
||||
}
|
||||
|
||||
|
||||
@@ -23,25 +23,26 @@ import UIKit
|
||||
|
||||
extension SPApp {
|
||||
|
||||
public struct Launch {
|
||||
enum Launch {
|
||||
|
||||
public static func run() {
|
||||
static func run() {
|
||||
self.count += 1
|
||||
}
|
||||
|
||||
public static var count: Int {
|
||||
get {
|
||||
return UserDefaults.standard.value(forKey: "SPLaunchCount") as? Int ?? 0
|
||||
}
|
||||
set {
|
||||
UserDefaults.standard.set(newValue, forKey: "SPLaunchCount")
|
||||
if (UserDefaults.standard.object(forKey: "SPDateFirstLaunch") as? Date) == nil {
|
||||
UserDefaults.standard.set(Date(), forKey: "SPDateFirstLaunch")
|
||||
}
|
||||
}
|
||||
|
||||
public static var isFirstLaunch: Bool {
|
||||
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() {}
|
||||
static var dateFirstLaunch: Date {
|
||||
return ((UserDefaults.standard.object(forKey: "SPDateFirstLaunch") as? Date) ?? Date())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import SafariServices
|
||||
|
||||
extension SPApp {
|
||||
|
||||
public static func open(app: SPSystemApp) {
|
||||
static func open(app: SPSystemApp) {
|
||||
switch app {
|
||||
case SPSystemApp.photos:
|
||||
guard let settingsUrl = URL(string: "photos-redirect://") else {
|
||||
@@ -63,21 +63,23 @@ extension SPApp {
|
||||
}
|
||||
}
|
||||
|
||||
public static func open(link: String, redirect: Bool) {
|
||||
static func open(link: String) {
|
||||
|
||||
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)
|
||||
}
|
||||
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
|
||||
}
|
||||
|
||||
static func open(link: String, on controller: UIViewController) {
|
||||
guard let url = URL(string: link) else {
|
||||
print("SPOpener - can not create URL")
|
||||
return
|
||||
}
|
||||
let safariController = SFSafariViewController.init(url: url)
|
||||
controller.present(safariController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@ import StoreKit
|
||||
|
||||
struct SPAppStore {
|
||||
|
||||
public static func link(appID: String) -> String {
|
||||
return "https://itunes.apple.com/by/app/id" + appID
|
||||
static func link(appID: String) -> String {
|
||||
return "https://itunes.apple.com/app/id" + appID
|
||||
}
|
||||
|
||||
public static func open(app id: String) {
|
||||
static func open(app id: String) {
|
||||
if let url = URL(string: "itms-apps://itunes.apple.com/app/id\(id)"),
|
||||
UIApplication.shared.canOpenURL(url) {
|
||||
if #available(iOS 10.0, *) {
|
||||
@@ -39,7 +39,7 @@ struct SPAppStore {
|
||||
}
|
||||
}
|
||||
|
||||
public static func requestReview(appID: String, force: Bool) {
|
||||
static func requestReview(appID: String, force: Bool) {
|
||||
if force {
|
||||
if let url = URL(string: "itms-apps://itunes.apple.com/us/app/apple-store/id\(appID)?mt=8&action=write-review"),
|
||||
UIApplication.shared.canOpenURL(url) {
|
||||
@@ -56,13 +56,13 @@ struct SPAppStore {
|
||||
}
|
||||
}
|
||||
|
||||
public static func requestReview() {
|
||||
static func requestReview() {
|
||||
if #available(iOS 10.3, *) {
|
||||
SKStoreReviewController.requestReview()
|
||||
}
|
||||
}
|
||||
|
||||
public static func isUpdateAvailable(completion: @escaping (Bool)->()) {
|
||||
static func isUpdateAvailable(completion: @escaping (Bool)->()) {
|
||||
|
||||
guard let info = Bundle.main.infoDictionary,
|
||||
let currentVersion = info["CFBundleShortVersionString"] as? String,
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
public struct SPAudio {
|
||||
struct SPAudio {
|
||||
|
||||
public static func notStopBackgroundMusic() {
|
||||
static func notStopBackgroundMusic() {
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category(rawValue: convertFromAVAudioSessionCategory(AVAudioSession.Category.ambient)), mode: AVAudioSession.Mode.default)
|
||||
try AVAudioSession.sharedInstance().setActive(true)
|
||||
|
||||
@@ -22,12 +22,12 @@
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
public class SPAudioPlayer: NSObject, AVAudioPlayerDelegate {
|
||||
class SPAudioPlayer: NSObject, AVAudioPlayerDelegate {
|
||||
|
||||
fileprivate var player: AVAudioPlayer = AVAudioPlayer()
|
||||
fileprivate var endPlayingComplection: (()->())? = nil
|
||||
|
||||
public func play(fileName: String, complection: (()->())? = nil) {
|
||||
func play(fileName: String, complection: (()->())? = nil, volume: Float = 1) {
|
||||
self.endPlayingComplection?()
|
||||
self.player = AVAudioPlayer()
|
||||
let url = Bundle.main.url(forResource: fileName, withExtension: nil)
|
||||
@@ -37,7 +37,7 @@ public class SPAudioPlayer: NSObject, AVAudioPlayerDelegate {
|
||||
}
|
||||
do {
|
||||
self.player = try AVAudioPlayer(contentsOf: url!)
|
||||
player.volume = 1
|
||||
player.volume = volume
|
||||
player.delegate = self
|
||||
player.prepareToPlay()
|
||||
player.play()
|
||||
@@ -47,11 +47,11 @@ public class SPAudioPlayer: NSObject, AVAudioPlayerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
public func stop() {
|
||||
func stop() {
|
||||
player.stop()
|
||||
}
|
||||
|
||||
public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
|
||||
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
|
||||
self.endPlayingComplection?()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
struct SPBufer {
|
||||
|
||||
public static var text: String? {
|
||||
static var text: String? {
|
||||
get {
|
||||
return UIPasteboard.general.string
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ import UIKit
|
||||
|
||||
extension SPCodeDraw {
|
||||
|
||||
public class AudioIconPack : NSObject {
|
||||
class AudioIconPack : NSObject {
|
||||
|
||||
@objc dynamic public class func drawPlay(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawPlay(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -53,7 +53,7 @@ extension SPCodeDraw {
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawPause(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawPause(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -90,7 +90,7 @@ extension SPCodeDraw {
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawStop(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawStop(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -118,13 +118,13 @@ extension SPCodeDraw {
|
||||
}
|
||||
|
||||
@objc(StyleKitNameResizingBehavior)
|
||||
public enum ResizingBehavior: Int {
|
||||
enum ResizingBehavior: Int {
|
||||
case aspectFit /// The content is proportionally resized to fit into the target rectangle.
|
||||
case aspectFill /// The content is proportionally resized to completely fill the target rectangle.
|
||||
case stretch /// The content is stretched to match the entire target rectangle.
|
||||
case center /// The content is centered in the target rectangle, but it is NOT resized.
|
||||
|
||||
public func apply(rect: CGRect, target: CGRect) -> CGRect {
|
||||
func apply(rect: CGRect, target: CGRect) -> CGRect {
|
||||
if rect == target || target == CGRect.zero {
|
||||
return rect
|
||||
}
|
||||
|
||||
@@ -21,4 +21,4 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public struct SPCodeDraw { private init() {} }
|
||||
struct SPCodeDraw { private init() {} }
|
||||
|
||||
@@ -23,9 +23,9 @@ import UIKit
|
||||
|
||||
extension SPCodeDraw {
|
||||
|
||||
public class SocialIconPack : NSObject {
|
||||
class SocialIconPack : NSObject {
|
||||
|
||||
@objc dynamic public class func drawInstagram(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawInstagram(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -115,7 +115,7 @@ extension SPCodeDraw {
|
||||
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawVK(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawVK(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -167,7 +167,7 @@ extension SPCodeDraw {
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawWhatsapp(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawWhatsapp(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -237,7 +237,7 @@ extension SPCodeDraw {
|
||||
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawTelegram(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawTelegram(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -313,7 +313,7 @@ extension SPCodeDraw {
|
||||
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawFacebook(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 41, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawFacebook(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 41, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -352,7 +352,7 @@ extension SPCodeDraw {
|
||||
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawViber(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 41, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawViber(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 41, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -443,13 +443,13 @@ extension SPCodeDraw {
|
||||
}
|
||||
|
||||
@objc(SocialIconStyleKitResizingBehavior)
|
||||
public enum ResizingBehavior: Int {
|
||||
enum ResizingBehavior: Int {
|
||||
case aspectFit /// The content is proportionally resized to fit into the target rectangle.
|
||||
case aspectFill /// The content is proportionally resized to completely fill the target rectangle.
|
||||
case stretch /// The content is stretched to match the entire target rectangle.
|
||||
case center /// The content is centered in the target rectangle, but it is NOT resized.
|
||||
|
||||
public func apply(rect: CGRect, target: CGRect) -> CGRect {
|
||||
func apply(rect: CGRect, target: CGRect) -> CGRect {
|
||||
if rect == target || target == CGRect.zero {
|
||||
return rect
|
||||
}
|
||||
|
||||
@@ -23,15 +23,15 @@ import UIKit
|
||||
|
||||
extension SPCodeDraw {
|
||||
|
||||
public class SystemIconPack : NSObject {
|
||||
class SystemIconPack : NSObject {
|
||||
|
||||
private struct Cache {
|
||||
static let gradient: CGGradient = CGGradient(colorsSpace: nil, colors: [UIColor.red.cgColor, UIColor.red.cgColor] as CFArray, locations: [0, 1])!
|
||||
}
|
||||
|
||||
@objc dynamic public class var gradient: CGGradient { return Cache.gradient }
|
||||
@objc dynamic class var gradient: CGGradient { return Cache.gradient }
|
||||
|
||||
@objc dynamic public class func drawFavorite(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawFavorite(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -80,7 +80,7 @@ extension SPCodeDraw {
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawFavoriteFill(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawFavoriteFill(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -113,7 +113,7 @@ extension SPCodeDraw {
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawShare(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawShare(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -154,7 +154,7 @@ extension SPCodeDraw {
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawClose(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawClose(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -192,13 +192,13 @@ extension SPCodeDraw {
|
||||
}
|
||||
|
||||
@objc(StyleKitNameResizingBehavior)
|
||||
public enum ResizingBehavior: Int {
|
||||
enum ResizingBehavior: Int {
|
||||
case aspectFit /// The content is proportionally resized to fit into the target rectangle.
|
||||
case aspectFill /// The content is proportionally resized to completely fill the target rectangle.
|
||||
case stretch /// The content is stretched to match the entire target rectangle.
|
||||
case center /// The content is centered in the target rectangle, but it is NOT resized.
|
||||
|
||||
public func apply(rect: CGRect, target: CGRect) -> CGRect {
|
||||
func apply(rect: CGRect, target: CGRect) -> CGRect {
|
||||
if rect == target || target == CGRect.zero {
|
||||
return rect
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ import UIKit
|
||||
|
||||
extension SPCodeDraw {
|
||||
|
||||
public class GolubevIconPack : NSObject {
|
||||
class GolubevIconPack : NSObject {
|
||||
|
||||
@objc dynamic public class func drawCamera(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawCamera(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -1494,7 +1494,7 @@ extension SPCodeDraw {
|
||||
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawPhotoLibrary(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawPhotoLibrary(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -2229,7 +2229,7 @@ extension SPCodeDraw {
|
||||
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawBall(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawBall(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -2855,7 +2855,7 @@ extension SPCodeDraw {
|
||||
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawCompass(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawCompass(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -3887,7 +3887,7 @@ extension SPCodeDraw {
|
||||
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawMicro(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawMicro(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -4864,7 +4864,7 @@ extension SPCodeDraw {
|
||||
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawBook(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawBook(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -5592,7 +5592,7 @@ extension SPCodeDraw {
|
||||
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawDocuments(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawDocuments(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -6068,7 +6068,7 @@ extension SPCodeDraw {
|
||||
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawCalendar(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawCalendar(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -7465,7 +7465,7 @@ extension SPCodeDraw {
|
||||
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawHeadphones(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawHeadphones(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -8248,7 +8248,7 @@ extension SPCodeDraw {
|
||||
|
||||
}
|
||||
|
||||
@objc dynamic public class func drawWindmill(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
@objc dynamic class func drawWindmill(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 1000, height: 1000), resizing: ResizingBehavior = .aspectFit, white: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000), light: UIColor = UIColor(red: 0.769, green: 0.847, blue: 0.984, alpha: 1.000), medium: UIColor = UIColor(red: 0.478, green: 0.663, blue: 0.973, alpha: 1.000), dark: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
|
||||
//// General Declarations
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
@@ -9119,13 +9119,13 @@ extension SPCodeDraw {
|
||||
|
||||
|
||||
@objc(StyleKitNameResizingBehavior)
|
||||
public enum ResizingBehavior: Int {
|
||||
enum ResizingBehavior: Int {
|
||||
case aspectFit /// The content is proportionally resized to fit into the target rectangle.
|
||||
case aspectFill /// The content is proportionally resized to completely fill the target rectangle.
|
||||
case stretch /// The content is stretched to match the entire target rectangle.
|
||||
case center /// The content is centered in the target rectangle, but it is NOT resized.
|
||||
|
||||
public func apply(rect: CGRect, target: CGRect) -> CGRect {
|
||||
func apply(rect: CGRect, target: CGRect) -> CGRect {
|
||||
if rect == target || target == CGRect.zero {
|
||||
return rect
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public struct SPConstraints {
|
||||
struct SPConstraints {
|
||||
|
||||
public static func setEqualSizeSuperview(for view: UIView) {
|
||||
static func setEqualSizeSuperview(for view: UIView) {
|
||||
if let superView = view.superview {
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
public func delay(_ delay:Double, closure: @escaping ()->()) {
|
||||
func delay(_ delay:Double, closure: @escaping ()->()) {
|
||||
let when = DispatchTime.now() + delay
|
||||
DispatchQueue.main.asyncAfter(deadline: when) {
|
||||
closure()
|
||||
|
||||
@@ -21,19 +21,19 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public struct SPDevice {
|
||||
struct SPDevice {
|
||||
|
||||
public static var iphone: Bool {
|
||||
static var iphone: Bool {
|
||||
return UIDevice.current.userInterfaceIdiom == .phone
|
||||
}
|
||||
|
||||
public static var ipad: Bool {
|
||||
static var ipad: Bool {
|
||||
return UIDevice.current.userInterfaceIdiom == .pad
|
||||
}
|
||||
|
||||
public struct Orientation {
|
||||
struct Orientation {
|
||||
|
||||
public static var isPortrait: Bool {
|
||||
static var isPortrait: Bool {
|
||||
var isPortraitOrientation = true
|
||||
if UIDevice.current.orientation.isValidInterfaceOrientation {
|
||||
if UIDevice.current.orientation.isPortrait {
|
||||
|
||||
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
struct SPDownloader {
|
||||
|
||||
public static func image(link: String, withComplection complection: @escaping (UIImage?) -> ()) {
|
||||
static func image(link: String, withComplection complection: @escaping (UIImage?) -> ()) {
|
||||
guard let url = URL(string: link) else {
|
||||
DispatchQueue.main.async {
|
||||
complection(nil)
|
||||
|
||||
@@ -23,7 +23,7 @@ import Foundation
|
||||
|
||||
extension Array {
|
||||
|
||||
public func get(count: Int) -> Array {
|
||||
func get(count: Int) -> Array {
|
||||
if (count < self.count) { return Array(self[0..<count]) }
|
||||
return Array(self)
|
||||
}
|
||||
@@ -31,7 +31,7 @@ extension Array {
|
||||
|
||||
extension Array where Element: Equatable {
|
||||
|
||||
public mutating func removeDuplicates() {
|
||||
mutating func removeDuplicates() {
|
||||
var result = [Element]()
|
||||
for value in self {
|
||||
if result.contains(value) == false { result.append(value) }
|
||||
@@ -42,8 +42,8 @@ extension Array where Element: Equatable {
|
||||
|
||||
extension Array where Element: Hashable {
|
||||
|
||||
public func after(item: Element) -> Element? {
|
||||
if let index = self.index(of: item), index + 1 < self.count { return self[index + 1] }
|
||||
func after(item: Element) -> Element? {
|
||||
if let index = self.firstIndex(of: item), index + 1 < self.count { return self[index + 1] }
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,29 +23,29 @@ import UIKit
|
||||
|
||||
extension CGRect {
|
||||
|
||||
public var bottomX: CGFloat {
|
||||
var bottomX: CGFloat {
|
||||
get { return self.origin.x + self.width }
|
||||
set { self.origin.x = newValue - self.width }
|
||||
}
|
||||
|
||||
public var bottomY: CGFloat {
|
||||
var bottomY: CGFloat {
|
||||
get { return self.origin.y + self.height }
|
||||
set { self.origin.y = newValue - self.height }
|
||||
}
|
||||
|
||||
public var minSide: CGFloat {
|
||||
var minSide: CGFloat {
|
||||
return min(self.width, self.height)
|
||||
}
|
||||
|
||||
public mutating func set(width: CGFloat) {
|
||||
mutating func set(width: CGFloat) {
|
||||
self = CGRect.init(x: self.origin.x, y: self.origin.y, width: width, height: self.height)
|
||||
}
|
||||
|
||||
public mutating func set(height: CGFloat) {
|
||||
mutating func set(height: CGFloat) {
|
||||
self = CGRect.init(x: self.origin.x, y: self.origin.y, width: self.width, height: height)
|
||||
}
|
||||
|
||||
public mutating func set(width: CGFloat, height: CGFloat) {
|
||||
mutating func set(width: CGFloat, height: CGFloat) {
|
||||
self = CGRect.init(x: self.origin.x, y: self.origin.y, width: width, height: height)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@ import UIKit
|
||||
|
||||
extension CGSize {
|
||||
|
||||
public func resize(width: CGFloat) -> CGSize {
|
||||
func resize(width: CGFloat) -> CGSize {
|
||||
let relativeSideSize = self.width / self.height
|
||||
let newHeight = width / relativeSideSize
|
||||
return CGSize.init(width: width, height: newHeight)
|
||||
}
|
||||
|
||||
public func resize(height: CGFloat) -> CGSize {
|
||||
func resize(height: CGFloat) -> CGSize {
|
||||
let relativeSideSize = self.width / self.height
|
||||
let newWidth = height * relativeSideSize
|
||||
return CGSize.init(width: newWidth, height: height)
|
||||
|
||||
@@ -23,16 +23,20 @@ import Foundation
|
||||
|
||||
extension Date {
|
||||
|
||||
public func format(mask: String) -> String {
|
||||
func format(mask: String) -> String {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = mask
|
||||
return dateFormatter.string(from: self)
|
||||
}
|
||||
|
||||
public static func create(from value: String) -> Date? {
|
||||
static func create(from value: String, mask: String = "dd.MM.yyyy HH:mm") -> Date? {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "dd.MM.yyyy HH:mm"
|
||||
formatter.dateFormat = mask
|
||||
let date = formatter.date(from: value)
|
||||
return date
|
||||
}
|
||||
|
||||
func add(days: Int) -> Date {
|
||||
return Calendar.current.date(byAdding: .day, value: days, to: self) ?? self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@ import Foundation
|
||||
|
||||
extension Strideable {
|
||||
|
||||
public mutating func setIfMore(when value: Self) {
|
||||
mutating func setIfMore(when value: Self) {
|
||||
if self > value {
|
||||
self = value
|
||||
}
|
||||
}
|
||||
|
||||
public mutating func setIfFewer(when value: Self) {
|
||||
mutating func setIfFewer(when value: Self) {
|
||||
if self < value {
|
||||
self = value
|
||||
}
|
||||
|
||||
@@ -24,65 +24,78 @@ import UIKit
|
||||
|
||||
extension String {
|
||||
|
||||
public var digits: String {
|
||||
var digits: String {
|
||||
return components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
|
||||
}
|
||||
|
||||
public mutating func dropLast(substring: String) {
|
||||
mutating func dropLast(substring: String) {
|
||||
if self.hasSuffix(substring) {
|
||||
self = String(dropLast(substring.count))
|
||||
}
|
||||
}
|
||||
|
||||
public mutating func dropFirst(substring: String) {
|
||||
mutating func dropFirst(substring: String) {
|
||||
if self.hasPrefix(substring) {
|
||||
self = String(dropFirst(substring.count))
|
||||
}
|
||||
}
|
||||
|
||||
public func uppercasedFirstLetter() -> String {
|
||||
func uppercasedFirstLetter() -> String {
|
||||
let lowercaseSctring = self.lowercased()
|
||||
return lowercaseSctring.prefix(1).uppercased() + lowercaseSctring.dropFirst()
|
||||
}
|
||||
|
||||
public mutating func uppercaseFirstLetter() {
|
||||
mutating func uppercaseFirstLetter() {
|
||||
self = self.uppercasedFirstLetter()
|
||||
}
|
||||
|
||||
public func removeAllSpaces() -> String {
|
||||
func removeAllSpaces() -> String {
|
||||
return self.components(separatedBy: .whitespaces).joined()
|
||||
}
|
||||
|
||||
public mutating func removeAllSpaces() {
|
||||
mutating func removeAllSpaces() {
|
||||
self = self.removeAllSpaces()
|
||||
}
|
||||
|
||||
public var isEmail: Bool {
|
||||
var isEmail: Bool {
|
||||
let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
|
||||
let emailTest = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
|
||||
return emailTest.evaluate(with: self)
|
||||
}
|
||||
|
||||
public var isLink: Bool {
|
||||
var isLink: Bool {
|
||||
if let url = URL(string: self) {
|
||||
return UIApplication.shared.canOpenURL(url)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public var isEmpty: Bool {
|
||||
var isEmptyContent: Bool {
|
||||
return (self.removeAllSpaces() == "")
|
||||
}
|
||||
|
||||
public var words: [String] {
|
||||
var words: [String] {
|
||||
return components(separatedBy: .punctuationCharacters).joined().components(separatedBy: .whitespaces)
|
||||
}
|
||||
|
||||
public mutating func replace(_ replacingString: String, with newString: String) {
|
||||
func removePrefix(_ prefix: String) -> String {
|
||||
guard self.hasPrefix(prefix) else { return self }
|
||||
return String(self.dropFirst(prefix.count))
|
||||
}
|
||||
|
||||
mutating func replace(_ replacingString: String, with newString: String) {
|
||||
self = self.replacingOccurrences(of: replacingString, with: newString)
|
||||
}
|
||||
|
||||
public func replace(_ replacingString: String, with newString: String) -> String {
|
||||
func replace(_ replacingString: String, with newString: String) -> String {
|
||||
return self.replacingOccurrences(of: replacingString, with: newString)
|
||||
}
|
||||
|
||||
func slice(from: String, to: String) -> String? {
|
||||
return (range(of: from)?.upperBound).flatMap { substringFrom in
|
||||
(range(of: to, range: substringFrom..<endIndex)?.lowerBound).map { substringTo in
|
||||
String(self[substringFrom..<substringTo])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
extension UITextField {
|
||||
|
||||
@IBInspectable public var placeholderColor: UIColor? {
|
||||
@IBInspectable var placeholderColor: UIColor? {
|
||||
get {
|
||||
return self.placeholderColor
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
extension UIAlertController {
|
||||
|
||||
public static var elementsColor: UIColor {
|
||||
static var elementsColor: UIColor {
|
||||
get {
|
||||
return UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor
|
||||
}
|
||||
@@ -32,19 +32,21 @@ extension UIAlertController {
|
||||
}
|
||||
}
|
||||
|
||||
public static func show(title: String, message: String, buttonTitle: String, cancelButtonTitle: String? = nil, complection: @escaping ()->() = {}, on viewController: UIViewController) {
|
||||
|
||||
static func show(title: String? = nil, message: String? = nil, buttonTitle: String, cancelButtonTitle: String? = nil, complection: @escaping ()->() = {}, on viewController: UIViewController) {
|
||||
let ac = UIAlertController(
|
||||
title: title,
|
||||
message: message,
|
||||
preferredStyle: .alert
|
||||
)
|
||||
|
||||
guard cancelButtonTitle != nil else { return }
|
||||
ac.addAction(UIAlertAction.init(
|
||||
title: cancelButtonTitle!,
|
||||
style: UIAlertAction.Style.cancel,
|
||||
handler: nil)
|
||||
)
|
||||
if let cancelTitle = cancelButtonTitle {
|
||||
ac.addAction(UIAlertAction.init(
|
||||
title: cancelTitle,
|
||||
style: UIAlertAction.Style.cancel,
|
||||
handler: nil)
|
||||
)
|
||||
}
|
||||
|
||||
ac.addAction(UIAlertAction.init(
|
||||
title: buttonTitle,
|
||||
@@ -56,7 +58,7 @@ extension UIAlertController {
|
||||
viewController.present(ac, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
public static func сonfirm(title: String? = nil, message: String, buttonTitle: String, cancelButtonTitle: String, isDestructive: Bool = false, complection: @escaping (Bool)->(), on viewController: UIViewController) {
|
||||
static func сonfirm(title: String? = nil, message: String? = nil, buttonTitle: String, cancelButtonTitle: String, isDestructive: Bool = false, complection: @escaping (Bool)->(), on viewController: UIViewController) {
|
||||
let ac = UIAlertController(
|
||||
title: title,
|
||||
message: message,
|
||||
@@ -83,21 +85,21 @@ extension UIAlertController {
|
||||
|
||||
extension UIAlertController {
|
||||
|
||||
public func addAction(title: String, complection: @escaping ()->()) {
|
||||
func addAction(title: String, complection: @escaping ()->()) {
|
||||
let action = UIAlertAction(title: title, style: .default) { (action) in
|
||||
complection()
|
||||
}
|
||||
self.addAction(action)
|
||||
}
|
||||
|
||||
public func addDestructiveAction(title: String, complection: @escaping ()->()) {
|
||||
func addDestructiveAction(title: String, complection: @escaping ()->()) {
|
||||
let action = UIAlertAction(title: title, style: .destructive) { (action) in
|
||||
complection()
|
||||
}
|
||||
self.addAction(action)
|
||||
}
|
||||
|
||||
public func addCancelAction(title: String, complection: @escaping ()->() = {}) {
|
||||
func addCancelAction(title: String, complection: @escaping ()->() = {}) {
|
||||
let action = UIAlertAction(title: title, style: .cancel) { (action) in
|
||||
complection()
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
extension UIButton {
|
||||
|
||||
public typealias UIButtonTargetClosure = () -> ()
|
||||
typealias UIButtonTargetClosure = () -> ()
|
||||
|
||||
private class ClosureWrapper: NSObject {
|
||||
let closure: UIButtonTargetClosure
|
||||
@@ -47,7 +47,7 @@ extension UIButton {
|
||||
}
|
||||
}
|
||||
|
||||
public func target(_ action: @escaping UIButtonTargetClosure) {
|
||||
func target(_ action: @escaping UIButtonTargetClosure) {
|
||||
targetClosure = action
|
||||
addTarget(self, action: #selector(UIButton.targetAction), for: .touchUpInside)
|
||||
}
|
||||
@@ -60,28 +60,28 @@ extension UIButton {
|
||||
|
||||
extension UIButton {
|
||||
|
||||
public func setTitle(_ title: String, color: UIColor? = nil) {
|
||||
func setTitle(_ title: String, color: UIColor? = nil) {
|
||||
self.setTitle(title, for: .normal)
|
||||
if let color = color {
|
||||
self.setTitleColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
public func setTitleColor(_ color: UIColor) {
|
||||
func setTitleColor(_ color: UIColor) {
|
||||
self.setTitleColor(color, for: .normal)
|
||||
self.setTitleColor(color.withAlphaComponent(0.7), for: .highlighted)
|
||||
}
|
||||
|
||||
public func setImage(_ image: UIImage) {
|
||||
func setImage(_ image: UIImage) {
|
||||
self.setImage(image, for: .normal)
|
||||
self.imageView?.contentMode = .scaleAspectFit
|
||||
}
|
||||
|
||||
public func removeAllTargets() {
|
||||
func removeAllTargets() {
|
||||
self.removeTarget(nil, action: nil, for: .allEvents)
|
||||
}
|
||||
|
||||
public func showText(_ text: String, withComplection completion: (() -> Void)! = {}) {
|
||||
func showText(_ text: String, withComplection completion: (() -> Void)! = {}) {
|
||||
let baseText = self.titleLabel?.text ?? " "
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
self.titleLabel?.alpha = 0
|
||||
@@ -105,7 +105,7 @@ extension UIButton {
|
||||
})
|
||||
}
|
||||
|
||||
public func setAnimatableText(_ text: String, withComplection completion: (() -> Void)! = {}) {
|
||||
func setAnimatableText(_ text: String, withComplection completion: (() -> Void)! = {}) {
|
||||
SPAnimation.animate(0.3, animations: {
|
||||
self.titleLabel?.alpha = 0
|
||||
}, withComplection: {
|
||||
@@ -118,7 +118,7 @@ extension UIButton {
|
||||
})
|
||||
}
|
||||
|
||||
public func hideContent(completion: (() -> Void)! = {}) {
|
||||
func hideContent(completion: (() -> Void)! = {}) {
|
||||
SPAnimation.animate(0.25, animations: {
|
||||
self.titleLabel?.alpha = 0
|
||||
}, withComplection: {
|
||||
@@ -126,7 +126,7 @@ extension UIButton {
|
||||
})
|
||||
}
|
||||
|
||||
public func showContent(completion: (() -> Void)! = {}) {
|
||||
func showContent(completion: (() -> Void)! = {}) {
|
||||
SPAnimation.animate(0.25, animations: {
|
||||
self.titleLabel?.alpha = 1
|
||||
}, withComplection: {
|
||||
|
||||
@@ -23,10 +23,34 @@ import UIKit
|
||||
|
||||
extension UICollectionView {
|
||||
|
||||
public var currentIndexCellPath: IndexPath? {
|
||||
var currentIndexCellPath: IndexPath? {
|
||||
let visibleRect = CGRect(origin: self.contentOffset, size: self.bounds.size)
|
||||
let visiblePoint = CGPoint.init(x: visibleRect.midX, y: visibleRect.midY)
|
||||
let visibleIndexPath = self.indexPathForItem(at: visiblePoint)
|
||||
return visibleIndexPath
|
||||
}
|
||||
|
||||
func lastRow(indexPath: IndexPath) -> Int {
|
||||
return self.numberOfItems(inSection: indexPath.section) - 1
|
||||
}
|
||||
|
||||
func isLastRow(indexPath: IndexPath) -> Bool {
|
||||
return indexPath.row == self.lastRow(indexPath: indexPath)
|
||||
}
|
||||
|
||||
func reloadData(animated: Bool, complection: @escaping ()->() = {}) {
|
||||
if animated {
|
||||
UIView.transition(
|
||||
with: self,
|
||||
duration: 0.3,
|
||||
options: [.transitionCrossDissolve, UIView.AnimationOptions.beginFromCurrentState],
|
||||
animations: {
|
||||
self.reloadData()
|
||||
}, completion: {(state) in
|
||||
complection()
|
||||
})
|
||||
} else {
|
||||
self.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIColor {
|
||||
extension UIColor {
|
||||
|
||||
public convenience init(hex: String) {
|
||||
convenience init(hex: String) {
|
||||
var red: CGFloat = 0.0
|
||||
var green: CGFloat = 0.0
|
||||
var blue: CGFloat = 0.0
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIFont {
|
||||
extension UIFont {
|
||||
|
||||
public static func system(weight: FontWeight, size: CGFloat) -> UIFont {
|
||||
static func system(weight: FontWeight, size: CGFloat) -> UIFont {
|
||||
return UIFont.systemFont(ofSize: size, weight: self.weight(for: weight))
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public extension UIFont {
|
||||
}
|
||||
}
|
||||
|
||||
public enum FontWeight {
|
||||
enum FontWeight {
|
||||
case regular
|
||||
case medium
|
||||
case light
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIImage {
|
||||
extension UIImage {
|
||||
|
||||
public func resize(width: CGFloat) -> UIImage {
|
||||
func resize(width: CGFloat) -> UIImage {
|
||||
let scale = width / self.size.width
|
||||
let newHeight = self.size.height * scale
|
||||
UIGraphicsBeginImageContext(CGSize(width: width, height: newHeight))
|
||||
|
||||
@@ -23,13 +23,13 @@ import UIKit
|
||||
|
||||
extension UIImageView {
|
||||
|
||||
public func setNative() {
|
||||
func setNative() {
|
||||
self.layer.borderWidth = 0.5
|
||||
self.layer.borderColor = SPNativeColors.midGray.cgColor
|
||||
self.layer.masksToBounds = true
|
||||
}
|
||||
|
||||
public func downloadedFrom(url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit, withComplection complection: @escaping (UIImage?) -> () = {_ in }) {
|
||||
func downloadedFrom(url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit, withComplection complection: @escaping (UIImage?) -> () = {_ in }) {
|
||||
DispatchQueue.main.async {
|
||||
self.contentMode = mode
|
||||
}
|
||||
@@ -47,7 +47,7 @@ extension UIImageView {
|
||||
}.resume()
|
||||
}
|
||||
|
||||
public func downloadedFrom(link: String, contentMode mode: UIView.ContentMode = .scaleAspectFit, withComplection complection: @escaping (UIImage?) -> () = {_ in }) {
|
||||
func downloadedFrom(link: String, contentMode mode: UIView.ContentMode = .scaleAspectFit, withComplection complection: @escaping (UIImage?) -> () = {_ in }) {
|
||||
guard let url = URL(string: link) else { return }
|
||||
downloadedFrom(url: url, contentMode: mode, withComplection: complection)
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UILabel {
|
||||
extension UILabel {
|
||||
|
||||
public func setShadowOffsetForLetters(blurRadius: CGFloat = 0, widthOffset: CGFloat = 0, heightOffset: CGFloat, opacity: CGFloat) {
|
||||
func setShadowOffsetForLetters(blurRadius: CGFloat = 0, widthOffset: CGFloat = 0, heightOffset: CGFloat, opacity: CGFloat) {
|
||||
self.layer.shadowRadius = blurRadius
|
||||
self.layer.shadowOffset = CGSize(
|
||||
width: widthOffset,
|
||||
@@ -32,29 +32,29 @@ public extension UILabel {
|
||||
self.layer.shadowOpacity = Float(opacity)
|
||||
}
|
||||
|
||||
public func setShadowOffsetFactorForLetters(blurRadius: CGFloat = 0, widthOffsetFactor: CGFloat = 0, heightOffsetFactor: CGFloat, opacity: CGFloat) {
|
||||
func setShadowOffsetFactorForLetters(blurRadius: CGFloat = 0, widthOffsetFactor: CGFloat = 0, heightOffsetFactor: CGFloat, opacity: CGFloat) {
|
||||
let widthOffset = widthOffsetFactor * self.frame.width
|
||||
let heightOffset = heightOffsetFactor * self.frame.height
|
||||
self.setShadowOffsetForLetters(blurRadius: blurRadius, widthOffset: widthOffset, heightOffset: heightOffset, opacity: opacity)
|
||||
}
|
||||
|
||||
public func removeShadowForLetters() {
|
||||
func removeShadowForLetters() {
|
||||
self.setShadowOffsetForLetters(blurRadius: 0, widthOffset: 0, heightOffset: 0, opacity: 0)
|
||||
}
|
||||
|
||||
public func setCenterAlignment() {
|
||||
func setCenterAlignment() {
|
||||
self.textAlignment = .center
|
||||
self.baselineAdjustment = .alignCenters
|
||||
}
|
||||
|
||||
public func setLettersSpacing(_ value: CGFloat) {
|
||||
func setLettersSpacing(_ value: CGFloat) {
|
||||
if let textString = text {
|
||||
let attrs: [NSAttributedString.Key : Any] = [.kern: value]
|
||||
attributedText = NSAttributedString(string: textString, attributes: attrs)
|
||||
}
|
||||
}
|
||||
|
||||
public func setLineSpacing(_ lineSpacing: CGFloat = 0.0, lineHeightMultiple: CGFloat = 0.0) {
|
||||
func setLineSpacing(_ lineSpacing: CGFloat = 0.0, lineHeightMultiple: CGFloat = 0.0) {
|
||||
|
||||
guard let labelText = self.text else { return }
|
||||
|
||||
@@ -62,7 +62,7 @@ public extension UILabel {
|
||||
paragraphStyle.lineSpacing = lineSpacing
|
||||
paragraphStyle.lineHeightMultiple = lineHeightMultiple
|
||||
|
||||
let attributedString:NSMutableAttributedString
|
||||
let attributedString: NSMutableAttributedString
|
||||
if let labelattributedText = self.attributedText {
|
||||
attributedString = NSMutableAttributedString(attributedString: labelattributedText)
|
||||
} else {
|
||||
@@ -73,4 +73,28 @@ public extension UILabel {
|
||||
|
||||
self.attributedText = attributedString
|
||||
}
|
||||
|
||||
func setFormat(text: String, positions: [FormatPosition], font: UIFont, textColor: UIColor, backgroundColor: UIColor = .clear) {
|
||||
|
||||
let title = NSMutableAttributedString.init(string: text)
|
||||
|
||||
for position in positions {
|
||||
title.addAttributes(
|
||||
[
|
||||
NSAttributedString.Key.backgroundColor : backgroundColor,
|
||||
NSAttributedString.Key.foregroundColor : textColor,
|
||||
NSAttributedString.Key.font : font
|
||||
],
|
||||
range: NSRange.init(location: position.start, length: position.length)
|
||||
)
|
||||
}
|
||||
|
||||
self.attributedText = title
|
||||
}
|
||||
|
||||
struct FormatPosition {
|
||||
|
||||
var start: Int
|
||||
var length: Int
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
extension UINavigationController {
|
||||
|
||||
public static var elementsColor: UIColor {
|
||||
static var elementsColor: UIColor {
|
||||
get {
|
||||
if UINavigationBar.appearance().tintColor != nil {
|
||||
return UINavigationBar.appearance().tintColor
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIScrollView {
|
||||
|
||||
func stopScrolling() {
|
||||
self.setContentOffset(self.contentOffset, animated: false)
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
extension UITabBarController {
|
||||
|
||||
public static var elementsColor: UIColor {
|
||||
static var elementsColor: UIColor {
|
||||
get {
|
||||
if UITabBar.appearance().tintColor != nil {
|
||||
return UITabBar.appearance().tintColor
|
||||
@@ -36,7 +36,7 @@ extension UITabBarController {
|
||||
}
|
||||
}
|
||||
|
||||
public func addTabBarItem(title: String, image: UIImage, selectedImage: UIImage? = nil, controller: UIViewController) {
|
||||
func addTabBarItem(title: String, image: UIImage, selectedImage: UIImage? = nil, controller: UIViewController) {
|
||||
|
||||
let tabBarItem = UITabBarItem(
|
||||
title: title,
|
||||
|
||||
@@ -23,19 +23,19 @@ import UIKit
|
||||
|
||||
extension UITableView {
|
||||
|
||||
public var isEmpty: Bool {
|
||||
var isEmpty: Bool {
|
||||
return self.lastSectionWithRows == nil
|
||||
}
|
||||
|
||||
public func isEmpty(section: Int) -> Bool {
|
||||
func isEmpty(section: Int) -> Bool {
|
||||
return self.numberOfRows(inSection: section) == 0
|
||||
}
|
||||
|
||||
public var lastSection: Int {
|
||||
var lastSection: Int {
|
||||
return self.numberOfSections - 1
|
||||
}
|
||||
|
||||
public var lastSectionWithRows: Int? {
|
||||
var lastSectionWithRows: Int? {
|
||||
if self.numberOfSections == 0 { return nil }
|
||||
var section = self.numberOfSections - 1
|
||||
if section < 0 { return nil }
|
||||
@@ -46,7 +46,7 @@ extension UITableView {
|
||||
return nil
|
||||
}
|
||||
|
||||
public var firstSectionWithRows: Int? {
|
||||
var firstSectionWithRows: Int? {
|
||||
if self.numberOfSections == 0 { return nil }
|
||||
var section = 0
|
||||
if section > self.numberOfSections - 1 { return nil }
|
||||
|
||||
@@ -23,11 +23,11 @@ import UIKit
|
||||
|
||||
extension UITableViewCell {
|
||||
|
||||
public var accessoryView: UIView? {
|
||||
var accessoryView: UIView? {
|
||||
return subviews.compactMap { $0 as? UIButton }.first
|
||||
}
|
||||
|
||||
public var highlightedColor: UIColor? {
|
||||
var highlightedColor: UIColor? {
|
||||
get {
|
||||
return self.backgroundView?.backgroundColor
|
||||
}
|
||||
@@ -38,7 +38,7 @@ extension UITableViewCell {
|
||||
}
|
||||
}
|
||||
|
||||
public func highlight() {
|
||||
func highlight() {
|
||||
self.setHighlighted(true, animated: false)
|
||||
self.setHighlighted(false, animated: true)
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
extension UITextField {
|
||||
|
||||
public var isEmptyText: Bool {
|
||||
var isEmptyText: Bool {
|
||||
get {
|
||||
if self.text == "" {
|
||||
return true
|
||||
|
||||
@@ -24,15 +24,15 @@ import Photos
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
public func present(_ viewControllerToPresent: UIViewController, completion: (() -> Swift.Void)? = nil) {
|
||||
func present(_ viewControllerToPresent: UIViewController, completion: (() -> Swift.Void)? = nil) {
|
||||
self.present(viewControllerToPresent, animated: true, completion: completion)
|
||||
}
|
||||
|
||||
@objc public func dismiss() {
|
||||
@objc func dismiss() {
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
public func wrapToNavigationController(statusBar: SPStatusBar = .dark) -> UINavigationController {
|
||||
func wrapToNavigationController(statusBar: SPStatusBar = .dark) -> UINavigationController {
|
||||
let controller = SPStatusBarManagerNavigationController(rootViewController: self)
|
||||
controller.statusBar = statusBar
|
||||
return controller
|
||||
@@ -41,20 +41,20 @@ extension UIViewController {
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
public func dismissKeyboardWhenTappedAround() {
|
||||
func dismissKeyboardWhenTappedAround() {
|
||||
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
|
||||
tap.cancelsTouchesInView = false
|
||||
view.addGestureRecognizer(tap)
|
||||
}
|
||||
|
||||
@objc public func dismissKeyboard() {
|
||||
@objc func dismissKeyboard() {
|
||||
view.endEditing(true)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
public func save(image: UIImage) {
|
||||
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 {
|
||||
}
|
||||
}
|
||||
|
||||
public func saveVideo(url: String, complection: @escaping (Bool)->()) {
|
||||
func saveVideo(url: String, complection: @escaping (Bool)->()) {
|
||||
DispatchQueue.global(qos: .utility).async {
|
||||
let urls = URL(string: url)
|
||||
let urldata = try? Data(contentsOf: urls!)
|
||||
@@ -90,7 +90,7 @@ extension UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
|
||||
@objc func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
|
||||
if let _ = error {
|
||||
self.imageSaved(isSuccses: false)
|
||||
} else {
|
||||
@@ -98,14 +98,14 @@ extension UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func imageSaved(isSuccses: Bool) {
|
||||
@objc func imageSaved(isSuccses: Bool) {
|
||||
fatalError("SPUIViewControllerExtenshion - Need ovveride 'imageSaved' func")
|
||||
}
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
public func setPrefersLargeNavigationTitle(_ title: String, smallScreenToSmallBar: Bool = true) {
|
||||
func setPrefersLargeNavigationTitle(_ title: String, smallScreenToSmallBar: Bool = true) {
|
||||
self.navigationItem.title = title
|
||||
if #available(iOS 11.0, *) {
|
||||
self.navigationItem.largeTitleDisplayMode = .automatic
|
||||
@@ -119,7 +119,7 @@ extension UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
public func setNavigationTitle(_ title: String, style: SPNavigationTitleStyle) {
|
||||
func setNavigationTitle(_ title: String, style: SPNavigationTitleStyle) {
|
||||
self.navigationItem.title = title
|
||||
switch style {
|
||||
case .large:
|
||||
@@ -135,13 +135,17 @@ extension UIViewController {
|
||||
if #available(iOS 11.0, *) {
|
||||
self.navigationItem.largeTitleDisplayMode = .never
|
||||
}
|
||||
case .noContent:
|
||||
if #available(iOS 11.0, *) {
|
||||
self.navigationItem.largeTitleDisplayMode = .never
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
public var safeArea: UIEdgeInsets {
|
||||
var safeArea: UIEdgeInsets {
|
||||
if #available(iOS 11.0, *) {
|
||||
return self.view.safeAreaInsets
|
||||
} else {
|
||||
@@ -149,18 +153,18 @@ extension UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
public var navigationBarHeight: CGFloat {
|
||||
var navigationBarHeight: CGFloat {
|
||||
return self.navigationController?.navigationBar.frame.height ?? 0
|
||||
}
|
||||
|
||||
public static var statusBarHeight: CGFloat {
|
||||
static var statusBarHeight: CGFloat {
|
||||
return UIApplication.shared.statusBarFrame.height
|
||||
}
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
public var navigationTitleColor: UIColor? {
|
||||
var navigationTitleColor: UIColor? {
|
||||
get {
|
||||
return (self.navigationController?.navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor) ?? nil
|
||||
}
|
||||
|
||||
@@ -21,9 +21,24 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIView {
|
||||
extension UIView {
|
||||
|
||||
public var viewController: UIViewController? {
|
||||
var isDarkMode: Bool {
|
||||
if #available(iOS 12.0, *) {
|
||||
if self.traitCollection.userInterfaceStyle == .dark {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
|
||||
var viewController: UIViewController? {
|
||||
get {
|
||||
if let nextResponder = self.next as? UIViewController { return nextResponder }
|
||||
else if let nextResponder = self.next as? UIView { return nextResponder.viewController }
|
||||
@@ -32,9 +47,9 @@ public extension UIView {
|
||||
}
|
||||
}
|
||||
|
||||
public extension UIView {
|
||||
extension UIView {
|
||||
|
||||
public var safeArea: UIEdgeInsets {
|
||||
var safeArea: UIEdgeInsets {
|
||||
if #available(iOS 11.0, *) {
|
||||
return self.safeAreaInsets
|
||||
} else{
|
||||
@@ -42,11 +57,11 @@ public extension UIView {
|
||||
}
|
||||
}
|
||||
|
||||
public func setBounds(_ view: UIView, withWidthFactor widthFactor: CGFloat = 1, maxWidth: CGFloat? = nil, withHeightFactor heightFactor: CGFloat = 1, maxHeight: CGFloat? = nil, withCentering: Bool = false) {
|
||||
func setBounds(_ view: UIView, withWidthFactor widthFactor: CGFloat = 1, maxWidth: CGFloat? = nil, withHeightFactor heightFactor: CGFloat = 1, maxHeight: CGFloat? = nil, withCentering: Bool = false) {
|
||||
self.setBounds(view.bounds, withWidthFactor: widthFactor, maxWidth: maxWidth, withHeightFactor: heightFactor, maxHeight: maxHeight, withCentering: withCentering)
|
||||
}
|
||||
|
||||
public func setBounds(_ bounds: CGRect, withWidthFactor widthFactor: CGFloat = 1, maxWidth: CGFloat? = nil, withHeightFactor heightFactor: CGFloat = 1, maxHeight: CGFloat? = nil, withCentering: Bool = false) {
|
||||
func setBounds(_ bounds: CGRect, withWidthFactor widthFactor: CGFloat = 1, maxWidth: CGFloat? = nil, withHeightFactor heightFactor: CGFloat = 1, maxHeight: CGFloat? = nil, withCentering: Bool = false) {
|
||||
|
||||
var width = bounds.width * widthFactor
|
||||
if maxWidth != nil { width.setIfMore(when: maxWidth!) }
|
||||
@@ -62,7 +77,7 @@ public extension UIView {
|
||||
}
|
||||
}
|
||||
|
||||
public func setSuperviewBounds(customWidth: CGFloat? = nil, customHeight: CGFloat? = nil) {
|
||||
func setSuperviewBounds(customWidth: CGFloat? = nil, customHeight: CGFloat? = nil) {
|
||||
if self.superview == nil { return }
|
||||
self.frame = CGRect.init(origin: CGPoint.zero, size: self.superview!.frame.size)
|
||||
if customWidth != nil {
|
||||
@@ -73,7 +88,7 @@ public extension UIView {
|
||||
}
|
||||
}
|
||||
|
||||
public func resize(width: CGFloat) {
|
||||
func resize(width: CGFloat) {
|
||||
let relativeFactor = self.frame.width / self.frame.height
|
||||
if relativeFactor.isNaN { return }
|
||||
self.frame = CGRect.init(
|
||||
@@ -84,7 +99,7 @@ public extension UIView {
|
||||
)
|
||||
}
|
||||
|
||||
public func resize(height: CGFloat) {
|
||||
func resize(height: CGFloat) {
|
||||
let relativeFactor = self.frame.width / self.frame.height
|
||||
if relativeFactor.isNaN { return }
|
||||
self.frame = CGRect.init(
|
||||
@@ -95,27 +110,27 @@ public extension UIView {
|
||||
)
|
||||
}
|
||||
|
||||
public func setYCenter() {
|
||||
func setYCenter() {
|
||||
self.center.y = (self.superview?.frame.height ?? 0) / 2
|
||||
}
|
||||
|
||||
public func setXCenter() {
|
||||
func setXCenter() {
|
||||
self.center.x = (self.superview?.frame.width ?? 0) / 2
|
||||
}
|
||||
|
||||
public func setToCenter() {
|
||||
func setToCenter() {
|
||||
self.center = CGPoint.init(x: ((self.superview?.frame.width) ?? 0) / 2, y: ((self.superview?.frame.height) ?? 0) / 2)
|
||||
}
|
||||
}
|
||||
|
||||
public extension UIView {
|
||||
extension UIView {
|
||||
|
||||
public func setParalax(amountFactor: CGFloat) {
|
||||
func setParalax(amountFactor: CGFloat) {
|
||||
let amount = self.frame.minSide * amountFactor
|
||||
self.setParalax(amount: amount)
|
||||
}
|
||||
|
||||
public func setParalax(amount: CGFloat) {
|
||||
func setParalax(amount: CGFloat) {
|
||||
self.motionEffects.removeAll()
|
||||
let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis)
|
||||
horizontal.minimumRelativeValue = -amount
|
||||
@@ -131,9 +146,9 @@ public extension UIView {
|
||||
}
|
||||
}
|
||||
|
||||
public extension UIView {
|
||||
extension UIView {
|
||||
|
||||
public func addGrade(alpha: CGFloat, color: UIColor = UIColor.black) -> UIView {
|
||||
func addGrade(alpha: CGFloat, color: UIColor = UIColor.black) -> UIView {
|
||||
let gradeView = UIView.init()
|
||||
gradeView.alpha = 0
|
||||
self.addSubview(gradeView)
|
||||
@@ -146,7 +161,7 @@ public extension UIView {
|
||||
|
||||
extension UIView {
|
||||
|
||||
public func setShadow(
|
||||
func setShadow(
|
||||
xTranslationFactor: CGFloat,
|
||||
yTranslationFactor: CGFloat,
|
||||
widthRelativeFactor: CGFloat,
|
||||
@@ -178,7 +193,7 @@ extension UIView {
|
||||
self.layer.shadowPath = shadowPath.cgPath;
|
||||
}
|
||||
|
||||
public func setShadow(
|
||||
func setShadow(
|
||||
xTranslation: CGFloat,
|
||||
yTranslation: CGFloat,
|
||||
widthRelativeFactor: CGFloat,
|
||||
@@ -203,14 +218,14 @@ extension UIView {
|
||||
self.layer.shadowPath = shadowPath.cgPath
|
||||
}
|
||||
|
||||
public func removeShadow() {
|
||||
func removeShadow() {
|
||||
self.layer.shadowColor = nil
|
||||
self.layer.shadowOffset = CGSize.zero
|
||||
self.layer.shadowOpacity = 0
|
||||
self.layer.shadowPath = nil
|
||||
}
|
||||
|
||||
public func addShadowOpacityAnimation(to: CGFloat, duration: CFTimeInterval) {
|
||||
func addShadowOpacityAnimation(to: CGFloat, duration: CFTimeInterval) {
|
||||
let animation = CABasicAnimation(keyPath:"shadowOpacity")
|
||||
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
|
||||
animation.fromValue = self.layer.cornerRadius
|
||||
@@ -224,7 +239,7 @@ extension UIView {
|
||||
|
||||
extension UIView {
|
||||
|
||||
public func addCornerRadiusAnimation(to: CGFloat, duration: CFTimeInterval) {
|
||||
func addCornerRadiusAnimation(to: CGFloat, duration: CFTimeInterval) {
|
||||
let animation = CABasicAnimation(keyPath:"cornerRadius")
|
||||
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
|
||||
animation.fromValue = self.layer.cornerRadius
|
||||
@@ -234,14 +249,14 @@ extension UIView {
|
||||
self.layer.cornerRadius = to
|
||||
}
|
||||
|
||||
public func show(duration: TimeInterval = 0.3) {
|
||||
func show(duration: TimeInterval = 0.3) {
|
||||
self.isHidden = false
|
||||
SPAnimation.animate(duration, animations: {
|
||||
self.alpha = 1
|
||||
})
|
||||
}
|
||||
|
||||
public func hide(duration: TimeInterval = 0.3) {
|
||||
func hide(duration: TimeInterval = 0.3) {
|
||||
SPAnimation.animate(duration, animations: {
|
||||
self.alpha = 0
|
||||
}, withComplection: {
|
||||
@@ -249,21 +264,21 @@ extension UIView {
|
||||
})
|
||||
}
|
||||
|
||||
public func removeAllAnimations() {
|
||||
func removeAllAnimations() {
|
||||
self.layer.removeAllAnimations()
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
|
||||
public func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
|
||||
func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
|
||||
let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
|
||||
let mask = CAShapeLayer()
|
||||
mask.path = path.cgPath
|
||||
self.layer.mask = mask
|
||||
}
|
||||
|
||||
public func round() {
|
||||
func round() {
|
||||
self.layer.cornerRadius = self.frame.minSide / 2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@ import UIKit
|
||||
|
||||
extension UIVisualEffectView {
|
||||
|
||||
public convenience init(style: UIBlurEffect.Style) {
|
||||
convenience init(style: UIBlurEffect.Style) {
|
||||
let effect = UIBlurEffect(style: style)
|
||||
self.init(effect: effect)
|
||||
}
|
||||
|
||||
public convenience init(vibrancy style: UIBlurEffect.Style) {
|
||||
convenience init(vibrancy style: UIBlurEffect.Style) {
|
||||
let effect = UIBlurEffect(style: style)
|
||||
let vibrancyEffect = UIVibrancyEffect(blurEffect: effect)
|
||||
self.init(effect: vibrancyEffect)
|
||||
|
||||
@@ -23,15 +23,15 @@ import Foundation
|
||||
|
||||
extension UserDefaults {
|
||||
|
||||
public func set(stringArray array: [String], forKey key: String) {
|
||||
func set(stringArray array: [String], forKey key: String) {
|
||||
self.set(array, forKey: key)
|
||||
}
|
||||
|
||||
public func set(boolArray array: [Bool], forKey key: String) {
|
||||
func set(boolArray array: [Bool], forKey key: String) {
|
||||
self.set(array, forKey: key)
|
||||
}
|
||||
|
||||
public func boolArray(forKey defaultName: String) -> [Bool] {
|
||||
func boolArray(forKey defaultName: String) -> [Bool] {
|
||||
return UserDefaults.standard.array(forKey: defaultName) as? [Bool] ?? []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public struct SPLayout {
|
||||
struct SPLayout {
|
||||
|
||||
public static func sizeWith(widthFactor: CGFloat, maxWidth: CGFloat?, heightFactor: CGFloat, maxHeight: CGFloat?, relativeSideFactor: CGFloat?, from size: CGSize) -> CGSize {
|
||||
static func sizeWith(widthFactor: CGFloat, maxWidth: CGFloat?, heightFactor: CGFloat, maxHeight: CGFloat?, relativeSideFactor: CGFloat?, from size: CGSize) -> CGSize {
|
||||
|
||||
var widthArea = size.width * widthFactor
|
||||
var heightArea = size.height * heightFactor
|
||||
|
||||
@@ -24,35 +24,16 @@ import LocalAuthentication
|
||||
|
||||
struct SPLocalAuthentication {
|
||||
|
||||
public static var isEnable: Bool {
|
||||
static var isEnable: Bool {
|
||||
let context = LAContext()
|
||||
var error: NSError?
|
||||
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
|
||||
return true
|
||||
} else {
|
||||
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error)
|
||||
}
|
||||
|
||||
public static func request(reason: String, complecton: @escaping (Bool)->()) {
|
||||
static func request(reason: String, complecton: @escaping (Bool)->()) {
|
||||
let context = LAContext()
|
||||
var error: NSError?
|
||||
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
|
||||
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, error in
|
||||
DispatchQueue.main.async { complecton(success) }
|
||||
}
|
||||
} else {
|
||||
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
|
||||
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) { success, error in
|
||||
DispatchQueue.main.async { complecton(success) }
|
||||
}
|
||||
} else {
|
||||
complecton(false)
|
||||
}
|
||||
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) { success, error in
|
||||
DispatchQueue.main.async { complecton(success) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public enum SPLocale: String, CaseIterable {
|
||||
enum SPLocale: String, CaseIterable {
|
||||
|
||||
case ru = "ru"
|
||||
case en = "en"
|
||||
|
||||
@@ -24,11 +24,11 @@ import MessageUI
|
||||
|
||||
struct SPMail {
|
||||
|
||||
public static var canSendEmail: Bool {
|
||||
static var canSendEmail: Bool {
|
||||
return MFMailComposeViewController.canSendMail()
|
||||
}
|
||||
|
||||
public static func openApp(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 {
|
||||
}
|
||||
}
|
||||
|
||||
public static func dialog(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
|
||||
|
||||
|
||||
@@ -21,21 +21,21 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public enum SPNativeColors {
|
||||
enum SPNativeColors {
|
||||
|
||||
public static let red = UIColor.init(hex: "FF3B30")
|
||||
public static let orange = UIColor.init(hex: "FF9500")
|
||||
public static let yellow = UIColor.init(hex: "FFCC00")
|
||||
public static let green = UIColor.init(hex: "4CD964")
|
||||
public static let tealBlue = UIColor.init(hex: "5AC8FA")
|
||||
public static let blue = UIColor.init(hex: "007AFF")
|
||||
public static let purple = UIColor.init(hex: "5856D6")
|
||||
public static let pink = UIColor.init(hex: "FF2D55")
|
||||
public static let white = UIColor.init(hex: "FFFFFF")
|
||||
public static let customGray = UIColor.init(hex: "EFEFF4")
|
||||
public static let lightGray = UIColor.init(hex: "E5E5EA")
|
||||
public static let lightGray2 = UIColor.init(hex: "D1D1D6")
|
||||
public static let midGray = UIColor.init(hex: "C7C7CC")
|
||||
public static let gray = UIColor.init(hex: "8E8E93")
|
||||
public static let black = UIColor.init(hex: "000000")
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -22,24 +22,28 @@
|
||||
import UIKit
|
||||
import UserNotifications
|
||||
|
||||
public struct SPLocalNotification {
|
||||
struct SPLocalNotification {
|
||||
|
||||
public static func add(from timeInterval: TimeInterval, body: String, title: String? = nil, identifier: String? = nil) {
|
||||
|
||||
let content = UNMutableNotificationContent()
|
||||
content.body = body
|
||||
content.title = title ?? ""
|
||||
content.badge = NSNumber(value: 1)
|
||||
content.sound = UNNotificationSound.default
|
||||
|
||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: false)
|
||||
|
||||
let identifier = identifier ?? "\(timeInterval)\(body)\(Int.random(min: 0, max: 1000))"
|
||||
var identificator: String? = nil
|
||||
var text: String
|
||||
var title: String? = nil
|
||||
var badge: Int = 0
|
||||
var timeInterval: TimeInterval
|
||||
var soundEnabled: Bool = true
|
||||
var category: SPLocalNotificationCategory? = nil
|
||||
|
||||
init(after timeInterval: TimeInterval, text: String) {
|
||||
self.text = text
|
||||
self.timeInterval = timeInterval
|
||||
}
|
||||
|
||||
func add() {
|
||||
let identificator = self.identificator ?? "\(self.timeInterval)\(self.text)\(Int.random(min: 0, max: 1000))"
|
||||
|
||||
let notification = UNNotificationRequest(
|
||||
identifier: identifier,
|
||||
content: content,
|
||||
trigger: trigger
|
||||
identifier: identificator,
|
||||
content: self.content,
|
||||
trigger: self.trigger
|
||||
)
|
||||
|
||||
let center = UNUserNotificationCenter.current()
|
||||
@@ -50,37 +54,36 @@ public struct SPLocalNotification {
|
||||
}
|
||||
}
|
||||
|
||||
public static func add(in date: Date, body: String, title: String? = nil, identifier: String? = nil) {
|
||||
|
||||
private var content: UNMutableNotificationContent {
|
||||
let content = UNMutableNotificationContent()
|
||||
content.body = body
|
||||
content.title = title ?? ""
|
||||
content.badge = NSNumber(value: 1)
|
||||
content.sound = UNNotificationSound.default
|
||||
content.body = self.text
|
||||
content.title = self.title ?? ""
|
||||
content.badge = NSNumber(value: UIApplication.shared.applicationIconBadgeNumber + self.badge)
|
||||
content.sound = self.soundEnabled ? UNNotificationSound.default : nil
|
||||
|
||||
let triggerDate = Calendar.current.dateComponents([.year,.month,.day,.hour,.minute,.second,], from: date)
|
||||
let trigger = UNCalendarNotificationTrigger.init(dateMatching: triggerDate, repeats: false)
|
||||
|
||||
let identifier = identifier ?? "\(date)\(body)\(Int.random(min: 0, max: 1000))"
|
||||
|
||||
let notification = UNNotificationRequest(
|
||||
identifier: identifier,
|
||||
content: content,
|
||||
trigger: trigger
|
||||
)
|
||||
|
||||
let center = UNUserNotificationCenter.current()
|
||||
center.add(notification) { (error) in
|
||||
if let error = error {
|
||||
print("SPLocalNotification - \(error)")
|
||||
if let category = self.category {
|
||||
if #available(iOS 12.0, *) {
|
||||
let notificationCategory = UNNotificationCategory(identifier: category.identifier, actions: category.actions, intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: nil, categorySummaryFormat: category.summary, options: [])
|
||||
UNUserNotificationCenter.current().setNotificationCategories([notificationCategory])
|
||||
content.categoryIdentifier = notificationCategory.identifier
|
||||
}
|
||||
}
|
||||
|
||||
return content
|
||||
}
|
||||
|
||||
public static func remove(identifier: String) {
|
||||
let center = UNUserNotificationCenter.current()
|
||||
center.removePendingNotificationRequests(withIdentifiers: [identifier])
|
||||
private var trigger: UNTimeIntervalNotificationTrigger {
|
||||
return UNTimeIntervalNotificationTrigger(timeInterval: self.timeInterval, repeats: false)
|
||||
}
|
||||
}
|
||||
|
||||
struct SPLocalNotificationCategory {
|
||||
|
||||
var identifier: String
|
||||
var summary: String
|
||||
var actions: [UNNotificationAction] = []
|
||||
|
||||
static var countSymbol: String {
|
||||
return "%u"
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension String {
|
||||
extension String {
|
||||
|
||||
public static func random(count: Int) -> String {
|
||||
static func random(count: Int) -> String {
|
||||
let strings = [
|
||||
"В доме кардинала от меня не было тайн; не раз видел я, как он усердно перелистывает старинные книги и жадно роется в пыли фамильных рукописей. Когда я как-то упрекнул его за бесполезные бессонные ночи, после которых он впадал в болезненное уныние, он взглянул на меня с горькой улыбкой и раскрыл передо мною историю города Рима. В этой книге, в двадцатой главе жизнеописания папы Александра Шестого, я прочел следующие строки, навсегда оставшиеся в моей памяти",
|
||||
"По этому поводу между отцом и сыном завязался спор. Цезарь считал, что достаточно применить одно из тех средств, которые он всегда держал наготове для своих ближайших друзей, а именно: пресловутый ключ, которым то одного, то другого просили отпереть некий шкаф. На ключе был крохотный железный шип – недосмотр слесаря. Каждый, кто трудился над тугим замком, накалывал себе палец и на другой день умирал. Был еще перстень с львиной головой, который Цезарь надевал, когда хотел пожать руку той или иной особе. Лев впивался в кожу этих избранных рук, и через сутки наступала смерть.",
|
||||
@@ -34,70 +34,61 @@ public extension String {
|
||||
}
|
||||
}
|
||||
|
||||
public extension Int {
|
||||
extension Int {
|
||||
|
||||
public static func random(_ n: Int) -> Int {
|
||||
return Int(arc4random_uniform(UInt32(n)))
|
||||
static func random() -> Int {
|
||||
return Int.random(in: 0...Int.max)
|
||||
}
|
||||
|
||||
public static func random(min: Int, max: Int) -> Int {
|
||||
return Int(arc4random_uniform(UInt32(max - min - 1))) + min
|
||||
static func random(min: Int, max: Int) -> Int {
|
||||
return Int.random(in: min...max)
|
||||
}
|
||||
}
|
||||
|
||||
public extension Double {
|
||||
extension Double {
|
||||
|
||||
public static func random() -> Double {
|
||||
return Double(arc4random()) / 0xFFFFFFFF
|
||||
static func random() -> Double {
|
||||
return Double.random(in: 0...Double.greatestFiniteMagnitude)
|
||||
}
|
||||
|
||||
public static func random(min: Double, max: Double) -> Double {
|
||||
return Double.random() * (max - min) + min
|
||||
static func random(min: Double, max: Double) -> Double {
|
||||
return Double.random(in: min...max)
|
||||
}
|
||||
}
|
||||
|
||||
public extension Float {
|
||||
extension Float {
|
||||
|
||||
public static func random() -> Float {
|
||||
return Float(arc4random()) / 0xFFFFFFFF
|
||||
static func random() -> Float {
|
||||
return Float.random(in: 0...Float.greatestFiniteMagnitude)
|
||||
}
|
||||
|
||||
public static func random(min: Float, max: Float) -> Float {
|
||||
return Float.random() * (max - min) + min
|
||||
static func random(min: Float, max: Float) -> Float {
|
||||
return Float.random(in: min...max)
|
||||
}
|
||||
}
|
||||
|
||||
public extension CGFloat {
|
||||
extension CGFloat {
|
||||
|
||||
public static func random() -> CGFloat {
|
||||
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
|
||||
static func random() -> CGFloat {
|
||||
return CGFloat.random(in: 0...CGFloat.greatestFiniteMagnitude)
|
||||
}
|
||||
|
||||
public static func random(min: CGFloat, max: CGFloat) -> CGFloat {
|
||||
return CGFloat.random() * (max - min) + min
|
||||
}
|
||||
}
|
||||
|
||||
public extension Collection {
|
||||
|
||||
public func shuffle() -> [Iterator.Element] {
|
||||
var list = Array(self)
|
||||
list.shuffleInPlace()
|
||||
return list
|
||||
static func random(min: CGFloat, max: CGFloat) -> CGFloat {
|
||||
return CGFloat.random(in: min...max)
|
||||
}
|
||||
}
|
||||
|
||||
extension Collection where Index == Int {
|
||||
|
||||
public func random() -> Iterator.Element? {
|
||||
func random() -> Iterator.Element? {
|
||||
return isEmpty ? nil : self[Int(arc4random_uniform(UInt32(endIndex)))]
|
||||
}
|
||||
}
|
||||
|
||||
public extension MutableCollection where Index == Int {
|
||||
|
||||
extension MutableCollection where Index == Int {
|
||||
|
||||
mutating func shuffleInPlace() {
|
||||
|
||||
|
||||
if count < 2 { return }
|
||||
|
||||
for i in startIndex ..< endIndex - 1 {
|
||||
|
||||
@@ -21,4 +21,4 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct SPShadow { private init() {} }
|
||||
struct SPShadow { private init() {} }
|
||||
|
||||
@@ -23,17 +23,17 @@ import UIKit
|
||||
|
||||
extension SPShadow {
|
||||
|
||||
public struct DeepStyle {
|
||||
struct DeepStyle {
|
||||
|
||||
private init() {}
|
||||
|
||||
public static func setFor(label: UILabel) {
|
||||
static func setFor(label: UILabel) {
|
||||
var offset = label.frame.height * 0.03
|
||||
offset.setIfMore(when: 1)
|
||||
label.setShadowOffsetForLetters(heightOffset: offset, opacity: 0.35)
|
||||
}
|
||||
|
||||
public static func setFor(view: UIView) {
|
||||
static func setFor(view: UIView) {
|
||||
|
||||
let xTranslationFactor: CGFloat = 0
|
||||
let yTranslationFactor: CGFloat = 0.18
|
||||
@@ -85,14 +85,14 @@ extension SPShadow {
|
||||
|
||||
extension UIView {
|
||||
|
||||
public func setDeepShadow() {
|
||||
func setDeepShadow() {
|
||||
SPShadow.DeepStyle.setFor(view: self)
|
||||
}
|
||||
}
|
||||
|
||||
extension UILabel {
|
||||
|
||||
public func setDeepShadowForLetters() {
|
||||
func setDeepShadowForLetters() {
|
||||
SPShadow.DeepStyle.setFor(label: self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public struct SPShare {
|
||||
struct SPShare {
|
||||
|
||||
public struct Native {
|
||||
struct Native {
|
||||
|
||||
public static func share(text: String? = nil, fileNames: [String] = [], images: [UIImage] = [], complection: ((_ isSharing: Bool)->())? = nil, sourceView: UIView, on viewController: UIViewController) {
|
||||
static func share(text: String? = nil, fileNames: [String] = [], images: [UIImage] = [], complection: ((_ isSharing: Bool)->())? = nil, sourceView: UIView, on viewController: UIViewController) {
|
||||
|
||||
var shareData: [Any] = []
|
||||
if text != nil {
|
||||
|
||||
@@ -21,31 +21,31 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPInstagram {
|
||||
class SPInstagram {
|
||||
|
||||
public static var isSetApp: Bool {
|
||||
static var isSetApp: Bool {
|
||||
return UIApplication.shared.canOpenURL(URL(string: "instagram://user?username=test")!)
|
||||
}
|
||||
|
||||
public static func openPost(id: String) {
|
||||
static func openPost(id: String) {
|
||||
let instagramHooks = "instagram://media?id=\(id)"
|
||||
let instagramUrl = URL(string: instagramHooks)
|
||||
let safariURL = URL(string: "instagram.com/\(id)")!
|
||||
if UIApplication.shared.canOpenURL(instagramUrl!) {
|
||||
UIApplication.shared.open(instagramUrl!, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
|
||||
} else {
|
||||
SPApp.open(link: safariURL.absoluteString, redirect: true)
|
||||
SPApp.open(link: safariURL.absoluteString)
|
||||
}
|
||||
}
|
||||
|
||||
public static func openUser(username: String) {
|
||||
static func openUser(username: String) {
|
||||
let instagramHooks = "instagram://user?username=\(username)"
|
||||
let instagramUrl = URL(string: instagramHooks)
|
||||
let safariURL = URL(string: "https://instagram.com/\(username)")!
|
||||
if UIApplication.shared.canOpenURL(instagramUrl!) {
|
||||
UIApplication.shared.open(instagramUrl!, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
|
||||
} else {
|
||||
SPApp.open(link: safariURL.absoluteString, redirect: true)
|
||||
SPApp.open(link: safariURL.absoluteString)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,13 +21,13 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPTelegram {
|
||||
class SPTelegram {
|
||||
|
||||
public static var isSetApp: Bool {
|
||||
static var isSetApp: Bool {
|
||||
return UIApplication.shared.canOpenURL(URL(string: "tg://msg?text=test")!)
|
||||
}
|
||||
|
||||
public static func share(text: String, complection: @escaping (_ isOpened: Bool)->() = {_ in }) {
|
||||
static func share(text: String, complection: @escaping (_ isOpened: Bool)->() = {_ in }) {
|
||||
let urlStringEncoded = text.addingPercentEncoding( withAllowedCharacters: .urlHostAllowed)
|
||||
let urlOptional = URL(string: "tg://msg?text=\(urlStringEncoded ?? "")")
|
||||
if let url = urlOptional {
|
||||
@@ -41,23 +41,33 @@ public class SPTelegram {
|
||||
}
|
||||
}
|
||||
|
||||
public static func joinChannel(id: String) {
|
||||
static func joinChannel(id: String) {
|
||||
let url = "https://t.me/joinchat/\(id)"
|
||||
SPApp.open(link: url, redirect: true)
|
||||
SPApp.open(link: url)
|
||||
}
|
||||
|
||||
public static func openBot(username: String) {
|
||||
static func joinChat(id: String) {
|
||||
let url = "https://t.me/joinchat/\(id)"
|
||||
SPApp.open(link: url)
|
||||
}
|
||||
|
||||
static func openBot(username: String) {
|
||||
var username = username
|
||||
if username.first == "@" {
|
||||
username.removeFirst()
|
||||
}
|
||||
let url = "https://telegram.me/\(username)"
|
||||
SPApp.open(link: url, redirect: true)
|
||||
SPApp.open(link: url)
|
||||
}
|
||||
|
||||
static func openDialog(username: String) {
|
||||
let url = "https://t.me/\(username)"
|
||||
SPApp.open(link: url)
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
|
||||
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
|
||||
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
|
||||
}
|
||||