Compare commits
289 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7612456ea9 | |||
| c44ab84eca | |||
| f08daeac2b | |||
| 9ac9e4374d | |||
| 3be7590fa2 | |||
| 801a6c8603 | |||
| e397952e6f | |||
| 314e83dd26 | |||
| 965667b373 | |||
| 9eea2c3259 | |||
| 7e58625b8e | |||
| 70a0a261cc | |||
| 214fe1cffd | |||
| 5222fc5a13 | |||
| b4bf86c56b | |||
| e0ba55a4a8 | |||
| 5696d657db | |||
| b698f106af | |||
| ccd0be4f01 | |||
| 682208fa21 | |||
| c7d9c7730a | |||
| dd43a80271 | |||
| 196c96a5d7 | |||
| dae24780b0 | |||
| ac33fc25e3 | |||
| a8f90dae76 | |||
| 1f34d8b641 | |||
| 5f6ea44f44 | |||
| c5a7709bef | |||
| 59f004625b | |||
| b9f02d317e | |||
| 29d3fe128e | |||
| 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 | |||
| ac366c84fd | |||
| 64bebe0645 | |||
| 1efa55aefb | |||
| e5371ca20c | |||
| fdbc1e3527 | |||
| 306547671c | |||
| 1fede9eb64 | |||
| 3ef2ff81bc | |||
| 83330bb509 | |||
| c4da6ff27e | |||
| 686bbd749b | |||
| 7b151fd268 | |||
| 7bdf702703 | |||
| 0d1381d941 | |||
| 39c30a030d | |||
| 9e4f6e086f | |||
| 5bc31cdc57 | |||
| 7d53ed31e6 | |||
| 80806de694 | |||
| 8d8458cc34 | |||
| 6e09df910e | |||
| 253f60cfa9 | |||
| 81ed7b01cd | |||
| d0f120d49c | |||
| bc3555e715 | |||
| 432a1206cf | |||
| 488b19fc46 | |||
| edc2b6ea41 | |||
| 79d64599d0 | |||
| 854ab2aef2 | |||
| 04c4929e7d | |||
| 6e4e9713c3 | |||
| e37c43c8f3 | |||
| 001536c835 | |||
| 5a7d01e1e4 | |||
| a1d17d994c | |||
| f56a054b91 | |||
| bd26c4d721 | |||
| 6927d90572 | |||
| ea32992f63 |
@@ -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 |
@@ -21,13 +21,15 @@ class Controller: UIViewController {
|
||||
self.presentTableControllerButton.addTarget(self, action: #selector(self.presentModalTableViewController), for: .touchUpInside)
|
||||
self.presentTableControllerButton.sizeToFit()
|
||||
self.presentTableControllerButton.center.x = self.view.frame.width / 2
|
||||
self.presentTableControllerButton.frame.origin.y = self.presentControllerButton.frame.bottomYPosition + 10
|
||||
self.presentTableControllerButton.frame.origin.y = self.presentControllerButton.frame.bottomY + 10
|
||||
self.view.addSubview(self.presentTableControllerButton)
|
||||
}
|
||||
|
||||
@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 Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// 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
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -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 closeButton: closeButtonPlace = .none {
|
||||
public var closeButtonPossition: CloseButtonPosition = .none {
|
||||
didSet {
|
||||
self.leftButton.titleLabel?.font = UIFont.system(type: .Regular, size: 17)
|
||||
self.rightButton.titleLabel?.font = UIFont.system(type: .Regular, size: 17)
|
||||
switch self.closeButton {
|
||||
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(type: .DemiBold, size: 17)
|
||||
self.leftButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
|
||||
case .right:
|
||||
self.rightButton.titleLabel?.font = UIFont.system(type: .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,38 +126,40 @@ public class SPFakeBarView: UIView {
|
||||
self.titleBottomConstraint?.isActive = true
|
||||
|
||||
self.addSubview(self.subtitleLabel)
|
||||
self.subtitleLabel.textColor = UIColor.init(hex: "8E8E92")
|
||||
self.subtitleLabel.font = UIFont.system(type: .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(type: .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(type: .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
|
||||
self.rightButton.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
|
||||
self.rightButton.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -12).isActive = true
|
||||
|
||||
self.closeButton = .none
|
||||
self.closeButtonPossition = .none
|
||||
|
||||
self.setContraints()
|
||||
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(type: .DemiBold, size: 17)
|
||||
self.titleLabel.setCenteringAlignment()
|
||||
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(type: .DemiBold, size: 17)
|
||||
self.titleLabel.setCenteringAlignment()
|
||||
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(type: .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()
|
||||
@@ -215,10 +226,25 @@ public class SPFakeBarView: UIView {
|
||||
self.updateConstraints()
|
||||
}
|
||||
|
||||
public enum closeButtonPlace {
|
||||
public enum CloseButtonPosition {
|
||||
case left
|
||||
case right
|
||||
case none
|
||||
}
|
||||
}
|
||||
|
||||
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() {}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
public var isPresentedAsStork: Bool {
|
||||
return transitioningDelegate is SPStorkTransitioningDelegate
|
||||
&& modalPresentationStyle == .custom
|
||||
&& presentingViewController != nil
|
||||
}
|
||||
|
||||
public func presentAsStork(_ controller: UIViewController, height: CGFloat? = nil) {
|
||||
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
|
||||
controller.modalPresentationCapturesStatusBarAppearance = true
|
||||
self.present(controller, animated: true, completion: complection)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// 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 Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// 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 Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// 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: AnyObject {
|
||||
|
||||
var needConfirm: Bool { get }
|
||||
|
||||
func confirm(_ completion: @escaping (_ isConfirmed: Bool)->())
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// 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: AnyObject {
|
||||
|
||||
@objc optional func didDismissStorkBySwipe()
|
||||
|
||||
@objc optional func didDismissStorkByTap()
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,26 +21,55 @@
|
||||
|
||||
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 }
|
||||
scrollView.subviews.forEach {
|
||||
$0.transform = CGAffineTransform(translationX: 0, y: -translation)
|
||||
}
|
||||
presentationController.setIndicator(style: scrollView.isTracking ? .line : .arrow)
|
||||
if translation >= presentationController.translateForDismiss * 0.4 {
|
||||
if !scrollView.isTracking && !scrollView.isDragging {
|
||||
self.dismissWithConfirmation(controller: controller, completion: {
|
||||
presentationController.storkDelegate?.didDismissStorkBySwipe?()
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
if presentationController.pan?.state != UIGestureRecognizer.State.changed {
|
||||
presentationController.scrollViewDidScroll(translation)
|
||||
presentationController.scrollViewDidScroll(translation * 2)
|
||||
}
|
||||
} else {
|
||||
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 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
|
||||
}
|
||||
|
||||
static public func updatePresentingController(parent controller: UIViewController) {
|
||||
if let presentationController = controller.presentedViewController?.presentationController as? SPStorkPresentationController {
|
||||
presentationController.updatePresentingController()
|
||||
@@ -53,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) {
|
||||
@@ -60,15 +102,4 @@ public struct SPStorkController {
|
||||
}
|
||||
return nextResponder as? UIViewController
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
var isPresentedAsStork: Bool {
|
||||
return transitioningDelegate is SPStorkTransitioningDelegate
|
||||
&& modalPresentationStyle == .custom
|
||||
&& presentingViewController != nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -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),
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,16 +23,25 @@ import UIKit
|
||||
|
||||
class SPStorkPresentationController: UIPresentationController, UIGestureRecognizerDelegate {
|
||||
|
||||
var isSwipeToDismissEnabled: Bool = true
|
||||
var isTapAroundToDismissEnabled: Bool = true
|
||||
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()
|
||||
@@ -42,53 +51,101 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
private var snapshotViewTopConstraint: NSLayoutConstraint?
|
||||
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
|
||||
return (statusBarHeight < 25) ? 30 : statusBarHeight
|
||||
}
|
||||
|
||||
private var alpha: CGFloat {
|
||||
return 0.51
|
||||
}
|
||||
|
||||
private var cornerRadius: CGFloat {
|
||||
return 10
|
||||
}
|
||||
private let alpha: CGFloat = 0.51
|
||||
var cornerRadius: CGFloat = 10
|
||||
|
||||
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.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
|
||||
|
||||
@@ -130,8 +187,9 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
containerView.insertSubview(snapshotView, aboveSubview: self.backgroundView)
|
||||
snapshotView.frame = initialFrame
|
||||
snapshotView.transform = transformForSnapshotView
|
||||
snapshotView.alpha = self.alpha
|
||||
snapshotView.alpha = 1 - self.alpha
|
||||
snapshotView.layer.cornerRadius = self.cornerRadius
|
||||
snapshotView.contentMode = .top
|
||||
snapshotView.layer.masksToBounds = true
|
||||
rootSnapshotView = snapshotView
|
||||
|
||||
@@ -154,6 +212,10 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
rootSnapshotView?.removeFromSuperview()
|
||||
rootSnapshotRoundedView?.removeFromSuperview()
|
||||
})
|
||||
|
||||
if self.hapticMoments.contains(.willPresent) {
|
||||
self.feedbackGenerator.impactOccurred()
|
||||
}
|
||||
}
|
||||
|
||||
override func presentationTransitionDidEnd(_ completed: Bool) {
|
||||
@@ -166,13 +228,13 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
self.snapshotViewContainer.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
|
||||
self.updateSnapshotAspectRatio()
|
||||
|
||||
if self.isTapAroundToDismissEnabled {
|
||||
self.tap = UITapGestureRecognizer.init(target: self, action: #selector(self.handleTap))
|
||||
if self.tapAroundToDismissEnabled {
|
||||
self.tap = UITapGestureRecognizer.init(target: self, action: #selector(self.tapArround))
|
||||
self.tap?.cancelsTouchesInView = false
|
||||
self.snapshotViewContainer.addGestureRecognizer(self.tap!)
|
||||
}
|
||||
|
||||
if self.isSwipeToDismissEnabled {
|
||||
if self.swipeToDismissEnabled {
|
||||
self.pan = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan))
|
||||
self.pan!.delegate = self
|
||||
self.pan!.maximumNumberOfTouches = 1
|
||||
@@ -181,17 +243,13 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
}
|
||||
}
|
||||
|
||||
@objc func handleTap() {
|
||||
self.presentedViewController.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
override func dismissalTransitionWillBegin() {
|
||||
super.dismissalTransitionWillBegin()
|
||||
guard let containerView = containerView else { return }
|
||||
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)
|
||||
@@ -220,6 +278,7 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
containerView.insertSubview(snapshotView, aboveSubview: backgroundView)
|
||||
snapshotView.frame = initialFrame
|
||||
snapshotView.transform = initialTransform
|
||||
snapshotView.contentMode = .top
|
||||
rootSnapshotView = snapshotView
|
||||
snapshotView.layer.cornerRadius = self.cornerRadius
|
||||
snapshotView.layer.masksToBounds = true
|
||||
@@ -227,7 +286,7 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
let snapshotRoundedView = UIView()
|
||||
snapshotRoundedView.layer.cornerRadius = self.cornerRadius
|
||||
snapshotRoundedView.layer.masksToBounds = true
|
||||
snapshotRoundedView.backgroundColor = UIColor.black.withAlphaComponent(1 - self.alpha)
|
||||
snapshotRoundedView.backgroundColor = UIColor.black.withAlphaComponent(self.alpha)
|
||||
containerView.insertSubview(snapshotRoundedView, aboveSubview: snapshotView)
|
||||
snapshotRoundedView.frame = initialFrame
|
||||
snapshotRoundedView.transform = initialTransform
|
||||
@@ -240,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()
|
||||
@@ -253,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
|
||||
@@ -262,8 +326,58 @@ 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(pan), self.isSwipeToDismissEnabled else { return }
|
||||
guard gestureRecognizer.isEqual(self.pan), self.swipeToDismissEnabled else { return }
|
||||
|
||||
switch gestureRecognizer.state {
|
||||
case .began:
|
||||
@@ -275,8 +389,8 @@ extension SPStorkPresentationController {
|
||||
gestureRecognizer.setTranslation(CGPoint(x: 0, y: 0), in: containerView)
|
||||
case .changed:
|
||||
self.workGester = true
|
||||
if self.isSwipeToDismissEnabled {
|
||||
let translation = gestureRecognizer.translation(in: presentedView)
|
||||
let translation = gestureRecognizer.translation(in: presentedView)
|
||||
if self.swipeToDismissEnabled {
|
||||
self.updatePresentedViewForTranslation(inVerticalDirection: translation.y)
|
||||
} else {
|
||||
gestureRecognizer.setTranslation(.zero, in: presentedView)
|
||||
@@ -284,9 +398,8 @@ extension SPStorkPresentationController {
|
||||
case .ended:
|
||||
self.workGester = false
|
||||
let translation = gestureRecognizer.translation(in: presentedView).y
|
||||
if translation >= 240 {
|
||||
presentedViewController.dismiss(animated: true, completion: nil)
|
||||
} else {
|
||||
|
||||
let toDefault = {
|
||||
self.indicatorView.style = .arrow
|
||||
UIView.animate(
|
||||
withDuration: 0.6,
|
||||
@@ -300,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)
|
||||
@@ -316,12 +445,32 @@ extension SPStorkPresentationController {
|
||||
self.updateSnapshot()
|
||||
}
|
||||
|
||||
func setIndicator(style: SPStorkIndicatorView.Style) {
|
||||
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 {
|
||||
@@ -335,9 +484,24 @@ extension SPStorkPresentationController {
|
||||
|
||||
self.presentedView?.transform = CGAffineTransform(translationX: 0, y: translationForModal)
|
||||
|
||||
let factor = 1 + (translationForModal / 6000)
|
||||
self.snapshotView?.transform = CGAffineTransform.init(scaleX: factor, y: factor)
|
||||
self.gradeView.alpha = self.alpha - ((factor - 1) * 15)
|
||||
let scaleFactor = 1 + (translationForModal / 5000)
|
||||
self.snapshotView?.transform = CGAffineTransform.init(scaleX: scaleFactor, y: scaleFactor)
|
||||
let gradeFactor = 1 + (translationForModal / 7000)
|
||||
self.gradeView.alpha = self.alpha - ((gradeFactor - 1) * 15)
|
||||
} 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -349,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,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()
|
||||
@@ -367,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() {
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,15 +25,14 @@ final class SPStorkPresentingAnimationController: NSObject, UIViewControllerAnim
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
|
||||
guard let presentedViewController = transitionContext.viewController(forKey: .to) else {
|
||||
return
|
||||
}
|
||||
guard let presentedViewController = transitionContext.viewController(forKey: .to) else { return }
|
||||
|
||||
let containerView = transitionContext.containerView
|
||||
containerView.addSubview(presentedViewController.view)
|
||||
presentedViewController.view.frame = CGRect(x: 0, y: containerView.bounds.height, width: containerView.bounds.width, height: containerView.bounds.height)
|
||||
|
||||
|
||||
let finalFrameForPresentedView = transitionContext.finalFrame(for: presentedViewController)
|
||||
presentedViewController.view.frame = finalFrameForPresentedView
|
||||
presentedViewController.view.frame.origin.y = containerView.bounds.height
|
||||
|
||||
UIView.animate(
|
||||
withDuration: transitionDuration(using: transitionContext),
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,20 +23,36 @@ import UIKit
|
||||
|
||||
public final class SPStorkTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
|
||||
|
||||
public var isSwipeToDismissEnabled: Bool = true
|
||||
public var isTapAroundToDismissEnabled: Bool = true
|
||||
public var swipeToDismissEnabled: Bool = true
|
||||
public var tapAroundToDismissEnabled: Bool = true
|
||||
public var 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.isSwipeToDismissEnabled = self.isSwipeToDismissEnabled
|
||||
controller.isTapAroundToDismissEnabled = self.isTapAroundToDismissEnabled
|
||||
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 Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,26 +21,30 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPEmptyProposeLabel: UILabel {
|
||||
open class SPStorkCloseView: UIView {
|
||||
|
||||
var color = UIColor.blue {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
super.init(frame: CGRect.zero)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
init(title: String) {
|
||||
super.init(frame: CGRect.zero)
|
||||
self.text = title
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
self.setCenteringAlignment()
|
||||
self.font = UIFont.system(type: .Regular, size: 14)
|
||||
self.textColor = SPNativeColors.gray
|
||||
self.backgroundColor = UIColor.clear
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -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.layer.removeAllAnimations()
|
||||
self.rightView.layer.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
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPAnimation {
|
||||
enum SPAnimation {
|
||||
|
||||
static func animate(_ duration: TimeInterval,
|
||||
animations: (() -> Void)!,
|
||||
@@ -47,7 +47,7 @@ public class SPAnimation {
|
||||
withComplection completion: (() -> Void)! = {}) {
|
||||
|
||||
var optionsWithRepeatition = options
|
||||
optionsWithRepeatition.insert([.autoreverse, .repeat])
|
||||
optionsWithRepeatition.insert([.autoreverse, .repeat, .allowUserInteraction])
|
||||
|
||||
self.animate(
|
||||
duration,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,11 +21,11 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPAnimationAlpha {
|
||||
class SPAnimationAlpha {
|
||||
|
||||
fileprivate static let durationListAnimation: TimeInterval = 0.45
|
||||
fileprivate static let coefLenthForTransition: CGFloat = 2.8
|
||||
fileprivate static let delayPerItem: TimeInterval = 0.09
|
||||
static let durationListAnimation: TimeInterval = 0.45
|
||||
static let coefLenthForTransition: CGFloat = 2.8
|
||||
static let delayPerItem: TimeInterval = 0.09
|
||||
|
||||
static func hideList(_ duration: TimeInterval = durationListAnimation,
|
||||
views: [UIView],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,10 +21,10 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPAnimationSpring {
|
||||
class SPAnimationSpring {
|
||||
|
||||
fileprivate static let spring: CGFloat = 1
|
||||
fileprivate static let velocity: CGFloat = 1
|
||||
static let spring: CGFloat = 1
|
||||
static let velocity: CGFloat = 1
|
||||
|
||||
static func animate(_ duration: TimeInterval,
|
||||
animations: (() -> Void)!,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,11 +21,11 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public class SPAnimationUpward {
|
||||
class SPAnimationUpward {
|
||||
|
||||
fileprivate static let durationListAnimation: TimeInterval = 0.45
|
||||
fileprivate static let coefLenthForTransition: CGFloat = 2.8
|
||||
fileprivate static let delayPerItem: TimeInterval = 0.09
|
||||
static let durationListAnimation: TimeInterval = 0.45
|
||||
static let coefLenthForTransition: CGFloat = 2.8
|
||||
static let delayPerItem: TimeInterval = 0.09
|
||||
|
||||
static func hide(_ duration: TimeInterval,
|
||||
view: UIView,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -20,9 +20,8 @@
|
||||
// SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
import StoreKit
|
||||
|
||||
struct SPApp {
|
||||
enum SPApp {
|
||||
|
||||
static var udid: String? {
|
||||
return UIDevice.current.identifierForVendor?.uuidString
|
||||
@@ -36,6 +35,14 @@ struct SPApp {
|
||||
return UIApplication.shared.keyWindow?.rootViewController
|
||||
}
|
||||
|
||||
static var safeArea: UIEdgeInsets {
|
||||
if #available(iOS 11.0, *) {
|
||||
return UIApplication.shared.keyWindow?.safeArea ?? UIEdgeInsets.zero
|
||||
} else {
|
||||
return UIEdgeInsets.zero
|
||||
}
|
||||
}
|
||||
|
||||
static func set(rootController: UIViewController, animatable: Bool = true) {
|
||||
|
||||
rootController.view.frame = UIScreen.main.bounds
|
||||
@@ -63,6 +70,4 @@ struct SPApp {
|
||||
UITabBarController.elementsColor = elementsColor
|
||||
UITabBar.appearance().tintColor = elementsColor
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
extension SPApp {
|
||||
|
||||
public struct Badge {
|
||||
struct Badge {
|
||||
|
||||
static var number: Int {
|
||||
get {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,25 +23,26 @@ import UIKit
|
||||
|
||||
extension SPApp {
|
||||
|
||||
struct Launch {
|
||||
enum Launch {
|
||||
|
||||
static func run() {
|
||||
self.count += 1
|
||||
if (UserDefaults.standard.object(forKey: "SPDateFirstLaunch") as? Date) == nil {
|
||||
UserDefaults.standard.set(Date(), forKey: "SPDateFirstLaunch")
|
||||
}
|
||||
}
|
||||
|
||||
static var count: Int {
|
||||
get {
|
||||
return UserDefaults.standard.value(forKey: "SPLaunchCount") as? Int ?? 0
|
||||
}
|
||||
set {
|
||||
UserDefaults.standard.set(newValue, forKey: "SPLaunchCount")
|
||||
}
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -63,21 +63,23 @@ extension SPApp {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,11 +25,11 @@ import StoreKit
|
||||
struct SPAppStore {
|
||||
|
||||
static func link(appID: String) -> String {
|
||||
return "https://itunes.apple.com/by/app/id" + appID
|
||||
return "https://itunes.apple.com/app/id" + appID
|
||||
}
|
||||
|
||||
static func open(appID: String) {
|
||||
if let url = URL(string: "itms-apps://itunes.apple.com/app/id\(appID)"),
|
||||
static func open(app id: String) {
|
||||
if let url = URL(string: "itms-apps://itunes.apple.com/app/id\(id)"),
|
||||
UIApplication.shared.canOpenURL(url) {
|
||||
if #available(iOS 10.0, *) {
|
||||
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -22,14 +22,14 @@
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
public struct SPAudio {
|
||||
struct SPAudio {
|
||||
|
||||
static func notStopBackgroundMusic() {
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category(rawValue: convertFromAVAudioSessionCategory(AVAudioSession.Category.ambient)), mode: AVAudioSession.Mode.default)
|
||||
try AVAudioSession.sharedInstance().setActive(true)
|
||||
} catch {
|
||||
|
||||
print("SPAudio - notStopBackgroundMusic, error")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -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
|
||||
|
||||
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()
|
||||
@@ -51,7 +51,7 @@ public class SPAudioPlayer: NSObject, AVAudioPlayerDelegate {
|
||||
player.stop()
|
||||
}
|
||||
|
||||
public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
|
||||
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
|
||||
self.endPlayingComplection?()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public struct SPConstraints {
|
||||
struct SPConstraints {
|
||||
|
||||
static func setEqualSizeSuperview(for view: UIView) {
|
||||
if let superView = view.superview {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -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()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -43,7 +43,7 @@ extension Array where Element: Equatable {
|
||||
extension Array where Element: Hashable {
|
||||
|
||||
func after(item: Element) -> Element? {
|
||||
if let index = self.index(of: item), index + 1 < self.count { return self[index + 1] }
|
||||
if let index = self.firstIndex(of: item), index + 1 < self.count { return self[index + 1] }
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,17 +23,29 @@ import UIKit
|
||||
|
||||
extension CGRect {
|
||||
|
||||
var bottomXPosition: CGFloat {
|
||||
var bottomX: CGFloat {
|
||||
get { return self.origin.x + self.width }
|
||||
set { self.origin.x = newValue - self.width }
|
||||
}
|
||||
|
||||
var bottomYPosition: CGFloat {
|
||||
var bottomY: CGFloat {
|
||||
get { return self.origin.y + self.height }
|
||||
set { self.origin.y = newValue - self.height }
|
||||
}
|
||||
|
||||
var minSideSize: CGFloat {
|
||||
var minSide: CGFloat {
|
||||
return min(self.width, self.height)
|
||||
}
|
||||
|
||||
mutating func set(width: CGFloat) {
|
||||
self = CGRect.init(x: self.origin.x, y: self.origin.y, width: width, height: self.height)
|
||||
}
|
||||
|
||||
mutating func set(height: CGFloat) {
|
||||
self = CGRect.init(x: self.origin.x, y: self.origin.y, width: self.width, height: height)
|
||||
}
|
||||
|
||||
mutating func set(width: CGFloat, height: CGFloat) {
|
||||
self = CGRect.init(x: self.origin.x, y: self.origin.y, width: width, height: height)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -29,10 +29,14 @@ extension Date {
|
||||
return dateFormatter.string(from: self)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -70,6 +70,19 @@ extension String {
|
||||
return false
|
||||
}
|
||||
|
||||
var isEmptyContent: Bool {
|
||||
return (self.removeAllSpaces() == "")
|
||||
}
|
||||
|
||||
var words: [String] {
|
||||
return components(separatedBy: .punctuationCharacters).joined().components(separatedBy: .whitespaces)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -77,4 +90,12 @@ extension 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])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -32,16 +32,17 @@ 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
|
||||
)
|
||||
|
||||
if cancelButtonTitle != nil {
|
||||
if let cancelTitle = cancelButtonTitle {
|
||||
ac.addAction(UIAlertAction.init(
|
||||
title: cancelButtonTitle!,
|
||||
title: cancelTitle,
|
||||
style: UIAlertAction.Style.cancel,
|
||||
handler: nil)
|
||||
)
|
||||
@@ -57,17 +58,15 @@ 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,
|
||||
preferredStyle: .actionSheet
|
||||
)
|
||||
|
||||
var style = UIAlertAction.Style.default
|
||||
if isDestructive {
|
||||
style = .destructive
|
||||
}
|
||||
let style = isDestructive ? .destructive : UIAlertAction.Style.default
|
||||
|
||||
ac.addAction(UIAlertAction.init(
|
||||
title: buttonTitle,
|
||||
style: style,
|
||||
@@ -76,11 +75,10 @@ extension UIAlertController {
|
||||
}))
|
||||
ac.addAction(UIAlertAction.init(
|
||||
title: cancelButtonTitle,
|
||||
style: UIAlertAction.Style.default,
|
||||
style: UIAlertAction.Style.cancel,
|
||||
handler: { (action) in
|
||||
complection(false)
|
||||
}))
|
||||
|
||||
viewController.present(ac, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
@@ -100,4 +98,11 @@ extension UIAlertController {
|
||||
}
|
||||
self.addAction(action)
|
||||
}
|
||||
|
||||
func addCancelAction(title: String, complection: @escaping ()->() = {}) {
|
||||
let action = UIAlertAction(title: title, style: .cancel) { (action) in
|
||||
complection()
|
||||
}
|
||||
self.addAction(action)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -60,8 +60,11 @@ extension UIButton {
|
||||
|
||||
extension UIButton {
|
||||
|
||||
func setTitle(_ title: String) {
|
||||
func setTitle(_ title: String, color: UIColor? = nil) {
|
||||
self.setTitle(title, for: .normal)
|
||||
if let color = color {
|
||||
self.setTitleColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
func setTitleColor(_ color: UIColor) {
|
||||
@@ -69,6 +72,11 @@ extension UIButton {
|
||||
self.setTitleColor(color.withAlphaComponent(0.7), for: .highlighted)
|
||||
}
|
||||
|
||||
func setImage(_ image: UIImage) {
|
||||
self.setImage(image, for: .normal)
|
||||
self.imageView?.contentMode = .scaleAspectFit
|
||||
}
|
||||
|
||||
func removeAllTargets() {
|
||||
self.removeTarget(nil, action: nil, for: .allEvents)
|
||||
}
|
||||
@@ -111,7 +119,7 @@ extension UIButton {
|
||||
}
|
||||
|
||||
func hideContent(completion: (() -> Void)! = {}) {
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
SPAnimation.animate(0.25, animations: {
|
||||
self.titleLabel?.alpha = 0
|
||||
}, withComplection: {
|
||||
completion()
|
||||
@@ -119,7 +127,7 @@ extension UIButton {
|
||||
}
|
||||
|
||||
func showContent(completion: (() -> Void)! = {}) {
|
||||
SPAnimation.animate(0.2, animations: {
|
||||
SPAnimation.animate(0.25, animations: {
|
||||
self.titleLabel?.alpha = 1
|
||||
}, withComplection: {
|
||||
completion()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -29,4 +29,28 @@ extension UICollectionView {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIColor {
|
||||
extension UIColor {
|
||||
|
||||
convenience init(hex: String) {
|
||||
var red: CGFloat = 0.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,42 +21,41 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIFont {
|
||||
extension UIFont {
|
||||
|
||||
public static func system(type: BoldType, size: CGFloat) -> UIFont {
|
||||
return UIFont.systemFont(ofSize: size, weight: self.getBoldTypeBy(boldType: type))
|
||||
static func system(weight: FontWeight, size: CGFloat) -> UIFont {
|
||||
return UIFont.systemFont(ofSize: size, weight: self.weight(for: weight))
|
||||
}
|
||||
|
||||
@available(iOS 8.2, *)
|
||||
private static func getBoldTypeBy(boldType: BoldType) -> UIFont.Weight {
|
||||
switch boldType {
|
||||
case .UltraLight:
|
||||
private static func weight(for weight: FontWeight) -> UIFont.Weight {
|
||||
switch weight {
|
||||
case .ultraLight:
|
||||
return UIFont.Weight.ultraLight
|
||||
case .Light:
|
||||
case .light:
|
||||
return UIFont.Weight.light
|
||||
case .Medium:
|
||||
case .medium:
|
||||
return UIFont.Weight.medium
|
||||
case .Regular:
|
||||
case .regular:
|
||||
return UIFont.Weight.regular
|
||||
case .Bold:
|
||||
case .bold:
|
||||
return UIFont.Weight.bold
|
||||
case .DemiBold:
|
||||
case .demiBold:
|
||||
return UIFont.Weight.semibold
|
||||
case .Heavy:
|
||||
case .heavy:
|
||||
return UIFont.Weight.heavy
|
||||
default:
|
||||
return UIFont.Weight.regular
|
||||
}
|
||||
}
|
||||
|
||||
public enum BoldType {
|
||||
case Regular
|
||||
case Medium
|
||||
case Light
|
||||
case UltraLight
|
||||
case Heavy
|
||||
case Bold
|
||||
case DemiBold
|
||||
case None
|
||||
enum FontWeight {
|
||||
case regular
|
||||
case medium
|
||||
case light
|
||||
case ultraLight
|
||||
case heavy
|
||||
case bold
|
||||
case demiBold
|
||||
case none
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -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))
|
||||
@@ -32,4 +32,12 @@ public extension UIImage {
|
||||
UIGraphicsEndImageContext()
|
||||
return newImage!
|
||||
}
|
||||
|
||||
var alwaysOriginal: UIImage {
|
||||
return self.withRenderingMode(.alwaysOriginal)
|
||||
}
|
||||
|
||||
var alwaysTemplate: UIImage {
|
||||
return self.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UILabel {
|
||||
extension UILabel {
|
||||
|
||||
func setShadowOffsetForLetters(blurRadius: CGFloat = 0, widthOffset: CGFloat = 0, heightOffset: CGFloat, opacity: CGFloat) {
|
||||
self.layer.shadowRadius = blurRadius
|
||||
@@ -42,7 +42,7 @@ public extension UILabel {
|
||||
self.setShadowOffsetForLetters(blurRadius: 0, widthOffset: 0, heightOffset: 0, opacity: 0)
|
||||
}
|
||||
|
||||
func setCenteringAlignment() {
|
||||
func setCenterAlignment() {
|
||||
self.textAlignment = .center
|
||||
self.baselineAdjustment = .alignCenters
|
||||
}
|
||||
@@ -54,7 +54,7 @@ public extension UILabel {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -71,7 +71,30 @@ public extension UILabel {
|
||||
|
||||
attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attributedString.length))
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -21,18 +21,9 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIViewController {
|
||||
extension UIScrollView {
|
||||
|
||||
public var isPresentedAsStork: Bool {
|
||||
return transitioningDelegate is SPStorkTransitioningDelegate
|
||||
&& modalPresentationStyle == .custom
|
||||
&& presentingViewController != nil
|
||||
}
|
||||
|
||||
public func presentAsStork(_ controller: UIViewController, complection: (() -> Void)? = nil) {
|
||||
let transitionDelegate = SPStorkTransitioningDelegate()
|
||||
controller.transitioningDelegate = transitionDelegate
|
||||
controller.modalPresentationStyle = .custom
|
||||
self.present(controller, animated: true, completion: complection)
|
||||
func stopScrolling() {
|
||||
self.setContentOffset(self.contentOffset, animated: false)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
extension UITableViewCell {
|
||||
|
||||
public var accessoryView: UIView? {
|
||||
var accessoryView: UIView? {
|
||||
return subviews.compactMap { $0 as? UIButton }.first
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ extension UITableViewCell {
|
||||
}
|
||||
}
|
||||
|
||||
public func highlight() {
|
||||
func highlight() {
|
||||
self.setHighlighted(true, animated: false)
|
||||
self.setHighlighted(false, animated: true)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -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!)
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,7 +21,22 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIView {
|
||||
extension UIView {
|
||||
|
||||
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 {
|
||||
@@ -32,7 +47,7 @@ public extension UIView {
|
||||
}
|
||||
}
|
||||
|
||||
public extension UIView {
|
||||
extension UIView {
|
||||
|
||||
var safeArea: UIEdgeInsets {
|
||||
if #available(iOS 11.0, *) {
|
||||
@@ -42,24 +57,11 @@ public extension UIView {
|
||||
}
|
||||
}
|
||||
|
||||
func set(width: CGFloat, height: CGFloat) {
|
||||
self.setHeight(height)
|
||||
self.setWidth(width)
|
||||
func setBounds(_ view: UIView, withWidthFactor widthFactor: CGFloat = 1, maxWidth: CGFloat? = nil, withHeightFactor heightFactor: CGFloat = 1, maxHeight: CGFloat? = nil, withCentering: Bool = false) {
|
||||
self.setBounds(view.bounds, withWidthFactor: widthFactor, maxWidth: maxWidth, withHeightFactor: heightFactor, maxHeight: maxHeight, withCentering: withCentering)
|
||||
}
|
||||
|
||||
func setHeight(_ height: CGFloat) {
|
||||
self.frame = CGRect.init(x: self.frame.origin.x, y: self.frame.origin.y, width: self.frame.width, height: height)
|
||||
}
|
||||
|
||||
func setWidth(_ width: CGFloat) {
|
||||
self.frame = CGRect.init(x: self.frame.origin.x, y: self.frame.origin.y, width: width, height: self.frame.height)
|
||||
}
|
||||
|
||||
func setEqualsFrameFromBounds(_ view: UIView, withWidthFactor widthFactor: CGFloat = 1, maxWidth: CGFloat? = nil, withHeightFactor heightFactor: CGFloat = 1, maxHeight: CGFloat? = nil, withCentering: Bool = false) {
|
||||
self.setEqualsFrameFromBounds(view.bounds, withWidthFactor: widthFactor, maxWidth: maxWidth, withHeightFactor: heightFactor, maxHeight: maxHeight, withCentering: withCentering)
|
||||
}
|
||||
|
||||
func setEqualsFrameFromBounds(_ bounds: CGRect, withWidthFactor widthFactor: CGFloat = 1, maxWidth: CGFloat? = nil, withHeightFactor heightFactor: CGFloat = 1, maxHeight: CGFloat? = nil, withCentering: Bool = false) {
|
||||
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!) }
|
||||
@@ -75,7 +77,7 @@ public extension UIView {
|
||||
}
|
||||
}
|
||||
|
||||
func setEqualsBoundsFromSuperview(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 {
|
||||
@@ -108,23 +110,23 @@ public extension UIView {
|
||||
)
|
||||
}
|
||||
|
||||
func setYCenteringFromSuperview() {
|
||||
func setYCenter() {
|
||||
self.center.y = (self.superview?.frame.height ?? 0) / 2
|
||||
}
|
||||
|
||||
func setXCenteringFromSuperview() {
|
||||
func setXCenter() {
|
||||
self.center.x = (self.superview?.frame.width ?? 0) / 2
|
||||
}
|
||||
|
||||
func setToCenterInSuperview() {
|
||||
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 {
|
||||
|
||||
func setParalax(amountFactor: CGFloat) {
|
||||
let amount = self.frame.minSideSize * amountFactor
|
||||
let amount = self.frame.minSide * amountFactor
|
||||
self.setParalax(amount: amount)
|
||||
}
|
||||
|
||||
@@ -144,7 +146,7 @@ public extension UIView {
|
||||
}
|
||||
}
|
||||
|
||||
public extension UIView {
|
||||
extension UIView {
|
||||
|
||||
func addGrade(alpha: CGFloat, color: UIColor = UIColor.black) -> UIView {
|
||||
let gradeView = UIView.init()
|
||||
@@ -174,14 +176,14 @@ extension UIView {
|
||||
let xTranslation = (self.frame.width - shadowWidth) / 2 + (self.frame.width * xTranslationFactor)
|
||||
let yTranslation = (self.frame.height - shadowHeight) / 2 + (self.frame.height * yTranslationFactor)
|
||||
|
||||
let cornerRadius = self.frame.minSideSize * cornerRadiusFactor
|
||||
let cornerRadius = self.frame.minSide * cornerRadiusFactor
|
||||
|
||||
let shadowPath = UIBezierPath.init(
|
||||
roundedRect: CGRect.init(x: xTranslation, y: yTranslation, width: shadowWidth, height: shadowHeight),
|
||||
cornerRadius: cornerRadius
|
||||
)
|
||||
|
||||
let blurRadius = self.frame.minSideSize * blurRadiusFactor
|
||||
let blurRadius = self.frame.minSide * blurRadiusFactor
|
||||
|
||||
self.layer.shadowColor = UIColor.black.cgColor
|
||||
self.layer.shadowOffset = CGSize.zero
|
||||
@@ -277,6 +279,6 @@ extension UIView {
|
||||
}
|
||||
|
||||
func round() {
|
||||
self.layer.cornerRadius = self.frame.minSideSize / 2
|
||||
self.layer.cornerRadius = self.frame.minSide / 2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -27,32 +27,13 @@ struct SPLocalAuthentication {
|
||||
static var isEnable: Bool {
|
||||
let context = LAContext()
|
||||
var error: NSError?
|
||||
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
|
||||
return true
|
||||
} else {
|
||||
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error)
|
||||
}
|
||||
|
||||
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) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -24,22 +24,26 @@ import UserNotifications
|
||||
|
||||
struct SPLocalNotification {
|
||||
|
||||
static func add(from timeInterval: TimeInterval, body: String, title: String? = nil, identifier: String? = nil) {
|
||||
|
||||
let content = UNMutableNotificationContent()
|
||||
content.body = body
|
||||
content.title = title ?? ""
|
||||
content.badge = NSNumber(value: 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 @@ struct SPLocalNotification {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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() {}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,8 +21,9 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension String {
|
||||
public static func random(count: Int) -> String {
|
||||
extension String {
|
||||
|
||||
static func random(count: Int) -> String {
|
||||
let strings = [
|
||||
"В доме кардинала от меня не было тайн; не раз видел я, как он усердно перелистывает старинные книги и жадно роется в пыли фамильных рукописей. Когда я как-то упрекнул его за бесполезные бессонные ночи, после которых он впадал в болезненное уныние, он взглянул на меня с горькой улыбкой и раскрыл передо мною историю города Рима. В этой книге, в двадцатой главе жизнеописания папы Александра Шестого, я прочел следующие строки, навсегда оставшиеся в моей памяти",
|
||||
"По этому поводу между отцом и сыном завязался спор. Цезарь считал, что достаточно применить одно из тех средств, которые он всегда держал наготове для своих ближайших друзей, а именно: пресловутый ключ, которым то одного, то другого просили отпереть некий шкаф. На ключе был крохотный железный шип – недосмотр слесаря. Каждый, кто трудился над тугим замком, накалывал себе палец и на другой день умирал. Был еще перстень с львиной головой, который Цезарь надевал, когда хотел пожать руку той или иной особе. Лев впивался в кожу этих избранных рук, и через сутки наступала смерть.",
|
||||
@@ -33,52 +34,47 @@ public extension String {
|
||||
}
|
||||
}
|
||||
|
||||
public extension Int {
|
||||
public static func random(_ n: Int) -> Int {
|
||||
return Int(arc4random_uniform(UInt32(n)))
|
||||
extension Int {
|
||||
|
||||
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 {
|
||||
public static func random() -> Double {
|
||||
return Double(arc4random()) / 0xFFFFFFFF
|
||||
extension Double {
|
||||
|
||||
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 {
|
||||
public static func random() -> Float {
|
||||
return Float(arc4random()) / 0xFFFFFFFF
|
||||
extension Float {
|
||||
|
||||
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 {
|
||||
public static func random() -> CGFloat {
|
||||
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
|
||||
extension CGFloat {
|
||||
|
||||
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 {
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,10 +85,10 @@ extension Collection where Index == Int {
|
||||
}
|
||||
}
|
||||
|
||||
public extension MutableCollection where Index == Int {
|
||||
|
||||
extension MutableCollection where Index == Int {
|
||||
|
||||
mutating func shuffleInPlace() {
|
||||
|
||||
|
||||
if count < 2 { return }
|
||||
|
||||
for i in startIndex ..< endIndex - 1 {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -27,13 +27,13 @@ extension SPShadow {
|
||||
|
||||
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
|
||||
@@ -66,7 +66,7 @@ extension SPShadow {
|
||||
yTranslation = view.frame.height + maxBottomSpace - shadowHeight
|
||||
}
|
||||
|
||||
var blurRadius = view.frame.minSideSize * blurRadiusFactor
|
||||
var blurRadius = view.frame.minSide * blurRadiusFactor
|
||||
blurRadius.setIfMore(when: 10)
|
||||
blurRadius.setIfFewer(when: 7)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public struct SPShare {
|
||||
struct SPShare {
|
||||
|
||||
public struct Native {
|
||||
struct Native {
|
||||
|
||||
static func share(text: String? = nil, fileNames: [String] = [], images: [UIImage] = [], complection: ((_ isSharing: Bool)->())? = nil, sourceView: UIView, on viewController: UIViewController) {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -34,7 +34,7 @@ class SPInstagram {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class SPInstagram {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -43,7 +43,12 @@ class SPTelegram {
|
||||
|
||||
static func joinChannel(id: String) {
|
||||
let url = "https://t.me/joinchat/\(id)"
|
||||
SPApp.open(link: url, redirect: true)
|
||||
SPApp.open(link: url)
|
||||
}
|
||||
|
||||
static func joinChat(id: String) {
|
||||
let url = "https://t.me/joinchat/\(id)"
|
||||
SPApp.open(link: url)
|
||||
}
|
||||
|
||||
static func openBot(username: String) {
|
||||
@@ -52,12 +57,17 @@ class SPTelegram {
|
||||
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)})
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||