Compare commits

...

130 Commits

Author SHA1 Message Date
Ivan Vorobei 71708b43c3 Update to 1.6.2
New width for parent controller.
2019-05-06 11:56:06 +03:00
Ivan Vorobei 66d7775d01 Update Example 2019-05-06 11:55:19 +03:00
Ivan Vorobei 298e0fdfc0 Update README.md 2019-05-05 18:04:59 +03:00
Ivan Vorobei 65070bce00 Update Example
And convert example project for swift 5 support.
2019-05-02 17:52:05 +03:00
Ivan Vorobei 2210afc7a7 Update README.md 2019-05-01 14:30:49 +03:00
Ivan Vorobei 94a8b06dde Update README.md 2019-04-30 20:58:58 +03:00
Ivan Vorobei 621e19cf78 Update README.md 2019-04-30 20:53:31 +03:00
Ivan Vorobei 6ae5e4f73d Update README.md 2019-04-30 16:31:35 +03:00
Ivan Vorobei 57d927bc49 Update README.md 2019-04-29 12:22:26 +03:00
Ivan Vorobei 1817c81bcc Update README.md 2019-04-29 12:20:49 +03:00
Ivan Vorobei 02e77c6074 Update README.md 2019-04-29 12:15:53 +03:00
Ivan Vorobei c922a8f522 Update README.md 2019-04-29 12:15:36 +03:00
Ivan Vorobei 86e7b3458d Update README.md 2019-04-29 12:15:00 +03:00
Ivan Vorobei a7ffa44434 Update README.md 2019-04-29 12:14:39 +03:00
Ivan Vorobei 96cc6b021a Update README.md 2019-04-29 12:14:16 +03:00
Ivan Vorobei 398da17b4a Update README.md 2019-04-28 23:05:55 +03:00
Ivan Vorobei 5e76f9f4d0 Update README.md 2019-04-28 18:52:54 +03:00
Ivan Vorobei a20a025a5b Update README.md 2019-04-28 18:47:39 +03:00
Ivan Vorobei 93e5b6f9de Update README.md 2019-04-28 18:46:27 +03:00
Ivan Vorobei 9d280b9b51 Update Readme 2019-04-28 16:28:53 +03:00
Ivan Vorobei 3d486c8f4b Update README.md 2019-04-26 18:39:12 +03:00
Ivan Vorobei d8fc226c9a Update README.md 2019-04-26 18:25:25 +03:00
Ivan Vorobei 7fd5eb41a6 Update Readme 2019-04-26 17:55:24 +03:00
Ivan Vorobei 263996e39d Update README.md 2019-04-26 17:53:52 +03:00
Ivan Vorobei b36cc7720d Update README.md 2019-04-26 17:53:38 +03:00
Ivan Vorobei f01ff0e904 Update Readme 2019-04-26 17:53:12 +03:00
Ivan Vorobei 92f29009c4 Update README.md 2019-04-26 17:45:06 +03:00
Ivan Vorobei b9813933b9 Update README.md 2019-04-26 17:36:54 +03:00
Ivan Vorobei ef0169a429 Update README.md 2019-04-26 17:35:52 +03:00
Ivan Vorobei 75766e6b7f Update README.md 2019-04-26 16:32:17 +03:00
Ivan Vorobei 9e8ed72013 Update README.md 2019-04-26 16:31:16 +03:00
Ivan Vorobei 29d6f3af93 Update README.md 2019-04-26 16:30:27 +03:00
Ivan Vorobei afa7e48e4c Update README.md 2019-04-26 16:20:25 +03:00
Ivan Vorobei b4c022e889 Update README.md 2019-04-26 16:13:36 +03:00
Ivan Vorobei 3c6e7e2c9c Update README.md 2019-04-26 15:55:05 +03:00
Ivan Vorobei 16e4685963 Update README.md 2019-04-26 15:53:29 +03:00
Ivan Vorobei e8e0e22259 Update README.md 2019-04-26 15:51:27 +03:00
Ivan Vorobei 19551282e6 Update Readme 2019-04-26 15:47:35 +03:00
Ivan Vorobei bc66345c6a Update Readme 2019-04-26 15:43:30 +03:00
Ivan Vorobei 73152ed8df Update README.md 2019-04-26 15:33:07 +03:00
Ivan Vorobei 431e9f58d5 Update README.md 2019-04-26 15:30:26 +03:00
Ivan Vorobei c3d75dac5e Update README.md 2019-04-26 15:30:01 +03:00
Ivan Vorobei cf9e7501d2 Update README.md 2019-04-26 15:27:47 +03:00
Ivan Vorobei 182ee5f0f0 Update README.md 2019-04-26 15:26:31 +03:00
Ivan Vorobei b2b37e717c Update README.md 2019-04-26 15:25:21 +03:00
Ivan Vorobei c0ea9d29a1 Update README.md 2019-04-26 15:21:57 +03:00
Ivan Vorobei cb55b4861a Update README.md 2019-04-26 15:20:57 +03:00
Ivan Vorobei 6a0aa725f7 Update README.md 2019-04-26 15:19:27 +03:00
Ivan Vorobei aebe62a9f2 Update README.md 2019-04-26 15:18:28 +03:00
Ivan Vorobei a0c0bf6885 Update README.md 2019-04-26 15:12:32 +03:00
Ivan Vorobei c0be873778 Update README.md 2019-04-26 15:11:04 +03:00
Ivan Vorobei 7fa550f79c Update README.md 2019-04-26 14:40:39 +03:00
Ivan Vorobei 6a22bb178b Update README.md 2019-04-25 16:57:19 +03:00
Ivan Vorobei 8993c2011d Update README.md 2019-04-24 12:03:09 +03:00
Ivan Vorobei 70199b65ee Update README.md 2019-04-23 12:40:06 +03:00
Ivan Vorobei 99ed2e84e1 Update README.md 2019-04-23 12:38:20 +03:00
Ivan Vorobei 6d8bf7c70a Update README.md 2019-04-23 12:26:31 +03:00
Ivan Vorobei a21dd49806 Update README.md 2019-04-23 12:05:22 +03:00
Ivan Vorobei 2cb195fcab Update README.md 2019-04-23 12:04:12 +03:00
Ivan Vorobei 4c78642040 Update README.md 2019-04-23 12:03:21 +03:00
Ivan Vorobei 73d297de19 Update README.md 2019-04-22 09:20:34 +03:00
Ivan Vorobei 1d167c9506 Update README.md 2019-04-22 09:19:19 +03:00
Ivan Vorobei 0b07e8e2cb Update README.md 2019-04-17 16:19:43 +03:00
Ivan Vorobei 8500571554 Update README.md 2019-04-17 16:11:08 +03:00
Ivan Vorobei 13134f2748 Update README.md 2019-04-17 16:05:39 +03:00
Ivan Vorobei 6e1a5c85b3 Update README.md 2019-04-17 16:04:01 +03:00
Ivan Vorobei eab9045cb5 Update README.md 2019-04-17 16:03:25 +03:00
Ivan Vorobei f567582b4d Update README.md 2019-04-17 16:00:48 +03:00
Ivan Vorobei 30365fde24 Fix protect level
Fix bug with `topScrollIndicatorInset` property, set `public` protect level. Add support in `podspec` swift `4.2` & `5.0`.
2019-04-17 00:49:40 +03:00
Ivan Vorobei 6b8fa51706 Update to 1.6.1
Add haptic feedback for some moments: see `hapticMoments`. Update example and Readme. Add support Carthage. Fix bug with func `removeAllAnimation` for `IndicatorView`.
2019-04-12 14:05:27 +03:00
Ivan Vorobei 1f1aa2ea8d Fix edit action
If translation < -50, indicator now force hide. Fix bug with edit actions for table view: implement `gestureRecognizerShouldBegin`.
2019-04-08 11:09:18 +03:00
Ivan Vorobei 914ce0f689 Fix bug with gester and EditingStyle for TableView
Implement `gestureRecognizerShouldBegin` func.
2019-04-05 23:40:00 +03:00
Ivan Vorobei 5dfab81504 Update SPStorkController.podspec 2019-04-04 14:10:29 +03:00
Ivan Vorobei 9270185bf6 Update SPStorkController.podspec 2019-04-04 14:06:45 +03:00
Ivan Vorobei 43bc6c27ee Update SPStorkController.podspec 2019-03-26 14:51:25 +03:00
Ivan Vorobei 551ccf3bf1 Update SPStorkController.podspec 2019-03-26 14:40:17 +03:00
Ivan Vorobei 0488521869 Update SPStorkController.podspec 2019-03-26 14:32:46 +03:00
Ivan Vorobei 9eae918f9d Update SPStorkController.podspec 2019-03-26 14:32:13 +03:00
Ivan Vorobei 28ed6b6ebc Update SPStorkController.podspec 2019-03-26 14:30:08 +03:00
Ivan Vorobei b783329b69 Update SPStorkController.podspec 2019-03-26 14:26:36 +03:00
Ivan Vorobei 84256152dc Update to 1.6
Update duration animation for show / hide indicator view when scrolling.
2019-03-26 14:19:27 +03:00
Ivan Vorobei ac366c84fd Update to 1.5.8 2019-03-25 23:30:30 +03:00
Ivan Vorobei 64bebe0645 Update to 1.5.7
Add `closeButton` and parametres.
2019-03-25 15:42:10 +03:00
Ivan Vorobei 1efa55aefb Update to 1.5.6
Add events delegate
2019-03-24 18:57:27 +03:00
Ivan Vorobei e5371ca20c Update to 1.5.5
Add new parameter `hideIndicatorWhenScroll` - it allow shows and hide indicator when scrolling. Also fixed bug when invalid frame for dismissing action.
2019-03-24 13:23:08 +03:00
Ivan Vorobei fdbc1e3527 Update README.md 2019-03-23 23:28:22 +03:00
Ivan Vorobei 306547671c Update README.md 2019-03-23 19:46:22 +03:00
Ivan Vorobei 1fede9eb64 Update to 1.5.4
Fix bug with dublicate indicator
2019-03-07 11:34:01 +03:00
Ivan Vorobei 3ef2ff81bc Update README.md 2019-03-04 15:34:07 +03:00
Ivan Vorobei 83330bb509 Update to 1.5.2 2019-03-03 18:48:30 +03:00
Ivan Vorobei c4da6ff27e Update to 1.5.1
Add `cornerRadius` property.
2019-03-01 16:54:54 +03:00
Ivan Vorobei 686bbd749b Add scroll indicator inset 2019-02-24 16:42:54 +03:00
Ivan Vorobei 7b151fd268 Update to 1.5
Change interactive parapetres. Fix logic for dismiss controller by drag down on `UIScrollView`
2019-02-20 01:23:55 +03:00
Ivan Vorobei 7bdf702703 Update README.md 2019-02-19 20:07:37 +03:00
Ivan Vorobei 0d1381d941 Update to 1.4.8
Change logic for dismiss controller when scroll down in `UIScrollView`.
2019-02-19 20:01:53 +03:00
Ivan Vorobei 39c30a030d Update SPStorkController.podspec 2019-02-19 12:31:58 +03:00
Ivan Vorobei 9e4f6e086f Update to 1.4.6
Added dismiss trigger when scrollView is scrolling down. Also added `swipeToDismissEnabled` checking to allow scrollView bounce when `swipeToDismissEnabled` is disabled.
2019-02-19 12:17:46 +03:00
Ivan Vorobei 5bc31cdc57 Merge pull request #37 from ilia3546/master
Added dismiss trigger when scrollView is scrolling down. Also added `swipeToDismissEnabled` checking to allow scrollView bounce when `swipeToDismissEnabled` is disabled.
2019-02-19 12:05:30 +03:00
Ilya Kharlamov 7d53ed31e6 Fix 2019-02-18 14:26:57 +03:00
Ilya Kharlamov 80806de694 Fix 2019-02-18 14:20:58 +03:00
Ilya Kharlamov 8d8458cc34 Revert "Custom width;"
This reverts commit dd2b29f27a.
2019-02-18 13:58:19 +03:00
Ilya Kharlamov 6e09df910e Custom width;
Ability to disable snapshot scaling;
2019-02-18 13:50:00 +03:00
Ilya Kharlamov 253f60cfa9 ScrollView with disabled swipeToDismissEnabled fixed;
Added trigger for dismissing when view is scrolling down;
2019-02-18 13:10:29 +03:00
Ivan Vorobei 81ed7b01cd Fix some bugs
Fix bug when present 2 or more controllers with custom heights. Also fix bug with grade for `presentingController` view.
2019-02-14 00:23:35 +03:00
Ivan Vorobei d0f120d49c Remove old code 2019-02-09 15:04:10 +03:00
Ivan Vorobei bc3555e715 Update README.md 2019-02-09 09:50:20 +03:00
Ivan Vorobei 432a1206cf Update README.md 2019-02-09 09:48:04 +03:00
Ivan Vorobei 488b19fc46 Update README.md 2019-02-09 09:29:22 +03:00
Ivan Vorobei edc2b6ea41 Fix example of table view 2019-02-09 09:27:13 +03:00
Ivan Vorobei 79d64599d0 Update SPStorkController.podspec 2019-02-08 19:02:59 +03:00
Ivan Vorobei 854ab2aef2 Fix bug
for update translation for scroll when presentng controller
2019-02-08 19:00:17 +03:00
Ivan Vorobei 04c4929e7d Update Readme.md 2019-02-08 18:20:47 +03:00
Ivan Vorobei 6e4e9713c3 Update example 2019-02-07 23:06:41 +03:00
Ivan Vorobei e37c43c8f3 Update SPStorkController.podspec 2019-02-07 11:45:07 +03:00
Ivan Vorobei 001536c835 Fix presented and dismiss action
Now if dismiss controller - keyboard will hide without delay. Also for func `presentAsStork` added property `height`. For SPStorkPresentingAnimationController fix start sizes
2019-02-06 10:53:58 +03:00
Ivan Vorobei 5a7d01e1e4 Update to 1.4.2
Add parameter `translateForDismiss`, which customise translation for dismiss controller. Default is `240`. Rename some parametrs.
2019-02-05 09:45:53 +03:00
Ivan Vorobei a1d17d994c Update to 1.4
If tap on `indicatorView`, controller will be close as in app Apple Music by Apple
2019-02-04 20:41:20 +03:00
Ivan Vorobei f56a054b91 Update SPStorkController.podspec 2019-02-04 01:41:40 +03:00
Ivan Vorobei bd26c4d721 Update SPStorkController.podspec 2019-02-04 01:39:58 +03:00
Ivan Vorobei 6927d90572 Update SPStorkController.podspec 2019-02-04 01:37:03 +03:00
Ivan Vorobei ea32992f63 Update to 1.2.8 2019-02-04 01:34:25 +03:00
Ivan Vorobei 05ba026c1f Update to 1.2.6
Add extenshion to UIViewController. Property `isPresentedAsStork` check if currenct controller present with this pod. For simple usage add func `presentAsStork` - need pass controller only.
2019-02-04 01:20:29 +03:00
Ivan Vorobei b3f49a2a94 Update SPStorkController.podspec 2019-02-01 03:15:56 +03:00
Ivan Vorobei c3aa4dc17e Update SPStorkController.podspec 2019-01-31 22:07:36 +03:00
Ivan Vorobei d9f67b57f8 Update SPStorkController.podspec 2019-01-31 21:49:03 +03:00
Ivan Vorobei 962d1a937d Update to 1.2.2
Add new parametr - `colorIndicator` for change color arrow. Update example & Readme. Fix `SPStorkIndicatorView `
2019-01-31 21:20:53 +03:00
Ivan Vorobei bc6c7ff45e Update README.md 2019-01-31 08:06:47 +03:00
Ivan Vorobei ec7abd4a2a Update example
Update `SparrowKit` pod
2019-01-26 14:01:36 +03:00
Ivan Vorobei 6904e0916a Update UserInterfaceState.xcuserstate 2019-01-21 16:33:48 +03:00
Ivan Vorobei 90ee3d35da Add navigation controller for example 2019-01-21 15:15:54 +03:00
146 changed files with 4915 additions and 1987 deletions
+37
View File
@@ -1 +1,38 @@
# 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/
# Carthage
Carthage/Build
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -7,7 +7,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.launch(rootViewController: Controller())
let navigationController = UINavigationController(rootViewController: Controller())
self.launch(rootViewController: navigationController)
return true
}
+3 -3
View File
@@ -21,7 +21,7 @@ class Controller: UIViewController {
self.presentTableControllerButton.addTarget(self, action: #selector(self.presentModalTableViewController), for: .touchUpInside)
self.presentTableControllerButton.sizeToFit()
self.presentTableControllerButton.center.x = self.view.frame.width / 2
self.presentTableControllerButton.frame.origin.y = self.presentControllerButton.frame.bottomYPosition + 10
self.presentTableControllerButton.frame.origin.y = self.presentControllerButton.frame.bottomY + 10
self.view.addSubview(self.presentTableControllerButton)
}
@@ -30,7 +30,7 @@ class Controller: UIViewController {
let transitionDelegate = SPStorkTransitioningDelegate()
modal.transitioningDelegate = transitionDelegate
modal.modalPresentationStyle = .custom
present(modal, animated: true, completion: nil)
self.present(modal, animated: true, completion: nil)
}
@objc func presentModalTableViewController() {
@@ -38,6 +38,6 @@ class Controller: UIViewController {
let transitionDelegate = SPStorkTransitioningDelegate()
modal.transitioningDelegate = transitionDelegate
modal.modalPresentationStyle = .custom
present(modal, animated: true, completion: nil)
self.present(modal, animated: true, completion: nil)
}
}
@@ -0,0 +1,30 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
public enum SPFakeBarNavigationStyle {
case large
case small
case stork
case noContent
}
@@ -21,9 +21,9 @@
import UIKit
public class SPFakeBarView: UIView {
open class SPFakeBarView: UIView {
public var style: SPNavigationTitleStyle = . small {
public var style: SPFakeBarNavigationStyle = .small {
didSet {
self.updateStyle()
}
@@ -32,7 +32,7 @@ public class SPFakeBarView: UIView {
private var settedHeight: CGFloat = 0
public var height: CGFloat {
get {
return (self.settedHeight) + (self.addStatusBarHeight ? UIViewController.statusBarHeight : 0)
return (self.settedHeight) + (self.addStatusBarHeight ? UIApplication.shared.statusBarFrame.height : 0)
}
set {
self.settedHeight = newValue
@@ -47,22 +47,24 @@ public class SPFakeBarView: UIView {
}
}
public var elementsColor: UIColor = UINavigationController.elementsColor {
public var elementsColor: UIColor = SPFakeBarView.navigationElementsColor {
didSet {
self.leftButton.setTitleColor(self.elementsColor)
self.rightButton.setTitleColor(self.elementsColor)
self.leftButton.setTitleColor(self.elementsColor, for: .normal)
self.leftButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
self.rightButton.setTitleColor(self.elementsColor, for: .normal)
self.rightButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
}
}
public var 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,52 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
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, hideIndicatorWhenScroll: Bool, showCloseButton: Bool, complection: (() -> Void)?) {
let transitionDelegate = SPStorkTransitioningDelegate()
transitionDelegate.customHeight = height
transitionDelegate.showCloseButton = showCloseButton
transitionDelegate.showIndicator = showIndicator
transitionDelegate.hideIndicatorWhenScroll = hideIndicatorWhenScroll
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 Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
public enum SPStorkHapticMoments {
case willPresent
case willDismiss
case willDismissIfRelease
}
@@ -0,0 +1,29 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
@objc public protocol SPStorkControllerDelegate: class {
@objc optional func didDismissStorkBySwipe()
@objc optional func didDismissStorkByTap()
}
+22 -10
View File
@@ -28,19 +28,40 @@ public struct SPStorkController {
if let presentationController = controller.presentationController as? SPStorkPresentationController {
let translation = -(scrollView.contentOffset.y + scrollView.contentInset.top)
if translation >= 0 {
if controller.isBeingPresented { return }
scrollView.subviews.forEach {
$0.transform = CGAffineTransform(translationX: 0, y: -translation)
}
presentationController.setIndicator(style: scrollView.isTracking ? .line : .arrow)
if translation >= presentationController.translateForDismiss * 0.4 {
if !scrollView.isTracking && !scrollView.isDragging {
presentationController.presentedViewController.dismiss(animated: true, 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 var topScrollIndicatorInset: CGFloat {
return 6
}
static public func updatePresentingController(parent controller: UIViewController) {
if let presentationController = controller.presentedViewController?.presentationController as? SPStorkPresentationController {
presentationController.updatePresentingController()
@@ -63,12 +84,3 @@ public struct SPStorkController {
private init() {}
}
extension UIViewController {
var isPresentedAsStork: Bool {
return transitioningDelegate is SPStorkTransitioningDelegate
&& modalPresentationStyle == .custom
&& presentingViewController != nil
}
}
@@ -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),
@@ -23,15 +23,23 @@ 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 customHeight: CGFloat? = nil
var translateForDismiss: CGFloat = 200
var hapticMoments: [SPStorkHapticMoments] = [.willDismissIfRelease]
var transitioningDelegate: SPStorkTransitioningDelegate?
weak var storkDelegate: SPStorkControllerDelegate?
var pan: UIPanGestureRecognizer?
var tap: UITapGestureRecognizer?
private var closeButton = SPStorkCloseButton()
private var indicatorView = SPStorkIndicatorView()
private var gradeView: UIView = UIView()
private let snapshotViewContainer = UIView()
@@ -41,53 +49,69 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
private var snapshotViewTopConstraint: NSLayoutConstraint?
private var snapshotViewWidthConstraint: NSLayoutConstraint?
private var snapshotViewAspectRatioConstraint: NSLayoutConstraint?
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 + 2) * 2 / containerView.frame.width)
return factor
}
private var feedbackGenerator: UIImpactFeedbackGenerator = UIImpactFeedbackGenerator(style: .light)
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 }
if self.showIndicator {
self.indicatorView.color = self.indicatorColor
let tap = UITapGestureRecognizer.init(target: self, action: #selector(self.dismissAction))
tap.cancelsTouchesInView = false
self.indicatorView.addGestureRecognizer(tap)
presentedView.addSubview(self.indicatorView)
}
self.updateLayoutIndicator()
self.indicatorView.style = .arrow
self.gradeView.alpha = 0
if self.showCloseButton {
self.closeButton.addTarget(self, action: #selector(self.dismissAction), for: .touchUpInside)
presentedView.addSubview(self.closeButton)
}
self.updateLayoutCloseButton()
let initialFrame: CGRect = presentingViewController.isPresentedAsStork ? presentingViewController.view.frame : containerView.bounds
containerView.insertSubview(self.snapshotViewContainer, belowSubview: presentedViewController.view)
@@ -128,8 +152,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
@@ -152,6 +177,10 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
rootSnapshotView?.removeFromSuperview()
rootSnapshotRoundedView?.removeFromSuperview()
})
if self.hapticMoments.contains(.willPresent) {
self.feedbackGenerator.impactOccurred()
}
}
override func presentationTransitionDidEnd(_ completed: Bool) {
@@ -164,13 +193,13 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
self.snapshotViewContainer.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
self.updateSnapshotAspectRatio()
if self.isTapAroundToDismissEnabled {
self.tap = UITapGestureRecognizer.init(target: self, action: #selector(self.handleTap))
if self.tapAroundToDismissEnabled {
self.tap = UITapGestureRecognizer.init(target: self, action: #selector(self.dismissAction))
self.tap?.cancelsTouchesInView = false
self.snapshotViewContainer.addGestureRecognizer(self.tap!)
}
if self.isSwipeToDismissEnabled {
if self.swipeToDismissEnabled {
self.pan = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan))
self.pan!.delegate = self
self.pan!.maximumNumberOfTouches = 1
@@ -179,8 +208,12 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
}
}
@objc func handleTap() {
self.presentedViewController.dismiss(animated: true, completion: nil)
@objc func dismissAction() {
self.presentingViewController.view.endEditing(true)
self.presentedViewController.view.endEditing(true)
self.presentedViewController.dismiss(animated: true, completion: {
self.storkDelegate?.didDismissStorkByTap?()
})
}
override func dismissalTransitionWillBegin() {
@@ -218,6 +251,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
@@ -225,7 +259,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
@@ -238,6 +272,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()
@@ -251,6 +288,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
@@ -261,18 +300,20 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
extension SPStorkPresentationController {
@objc func handlePan(gestureRecognizer: UIPanGestureRecognizer) {
guard gestureRecognizer.isEqual(pan), self.isSwipeToDismissEnabled else { return }
guard gestureRecognizer.isEqual(self.pan), self.swipeToDismissEnabled else { return }
switch gestureRecognizer.state {
case .began:
self.workGester = true
self.indicatorView.style = .line
self.presentingViewController.view.layer.removeAllAnimations()
self.presentingViewController.view.endEditing(true)
self.presentedViewController.view.endEditing(true)
gestureRecognizer.setTranslation(CGPoint(x: 0, y: 0), in: containerView)
case .changed:
self.workGester = true
if self.isSwipeToDismissEnabled {
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)
@@ -280,8 +321,10 @@ extension SPStorkPresentationController {
case .ended:
self.workGester = false
let translation = gestureRecognizer.translation(in: presentedView).y
if translation >= 240 {
presentedViewController.dismiss(animated: true, completion: nil)
if translation >= self.translateForDismiss {
self.presentedViewController.dismiss(animated: true, completion: {
self.storkDelegate?.didDismissStorkBySwipe?()
})
} else {
self.indicatorView.style = .arrow
UIView.animate(
@@ -301,6 +344,14 @@ extension SPStorkPresentationController {
}
}
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)
@@ -312,6 +363,26 @@ 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 }
@@ -331,9 +402,22 @@ 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.hapticMoments.contains(.willDismissIfRelease) {
self.feedbackGenerator.impactOccurred()
}
}
}
}
}
@@ -356,6 +440,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()
@@ -370,6 +455,12 @@ extension SPStorkPresentationController {
self.indicatorView.center.x = presentedView.frame.width / 2
}
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() {
guard let currentSnapshotView = presentingViewController.view.snapshotView(afterScreenUpdates: true) else { return }
self.snapshotView?.removeFromSuperview()
@@ -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),
@@ -23,18 +23,32 @@ 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 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 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.customHeight = self.customHeight
controller.translateForDismiss = self.translateForDismiss
controller.cornerRadius = self.cornerRadius
controller.hapticMoments = self.hapticMoments
controller.transitioningDelegate = self
controller.storkDelegate = self.storkDelegate
return controller
}
@@ -0,0 +1,78 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
open class SPStorkCloseButton: UIButton {
let iconView = SPStorkCloseView()
var widthIconFactor: CGFloat = 1
var heightIconFactor: CGFloat = 1
var color = UIColor.blue {
didSet {
self.iconView.color = self.color
}
}
override open var isHighlighted: Bool {
didSet {
self.iconView.color = self.color.withAlphaComponent(self.isHighlighted ? 0.7 : 1)
}
}
init() {
super.init(frame: .zero)
self.commonInit()
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func commonInit() {
self.iconView.isUserInteractionEnabled = false
self.addSubview(self.iconView)
self.backgroundColor = UIColor.init(red: 239 / 255.0, green: 239 / 255.0, blue: 244 / 255.0, alpha: 1)
self.color = UIColor.init(red: 142 / 255.0, green: 142 / 255.0, blue: 147 / 255.0, alpha: 1)
self.widthIconFactor = 0.36
self.heightIconFactor = 0.36
}
func layout(bottomX: CGFloat, y: CGFloat) {
self.sizeToFit()
self.frame.origin.x = bottomX - self.frame.width
self.frame.origin.y = y
}
override open func layoutSubviews() {
super.layoutSubviews()
self.layer.cornerRadius = self.frame.width / 2
self.iconView.frame = CGRect.init(x: 0, y: 0, width: self.frame.width * self.widthIconFactor, height: self.frame.height * self.heightIconFactor)
self.iconView.center = CGPoint.init(x: self.frame.width / 2, y: self.frame.height / 2)
}
override open func sizeToFit() {
super.sizeToFit()
self.frame = CGRect.init(x: self.frame.origin.x, y: self.frame.origin.y, width: 30, height: 30)
}
}
@@ -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)
}
}
@@ -21,7 +21,7 @@
import UIKit
class SPStorkIndicatorView: UIView {
open class SPStorkIndicatorView: UIView {
var style: Style = .line {
didSet {
@@ -42,6 +42,13 @@ class SPStorkIndicatorView: UIView {
}
}
var color: UIColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1) {
didSet {
self.leftView.backgroundColor = self.color
self.rightView.backgroundColor = self.color
}
}
private var leftView: UIView = UIView()
private var rightView: UIView = UIView()
@@ -50,15 +57,14 @@ class SPStorkIndicatorView: UIView {
self.backgroundColor = UIColor.clear
self.addSubview(self.leftView)
self.addSubview(self.rightView)
self.leftView.backgroundColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
self.rightView.backgroundColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
self.color = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
}
required init?(coder aDecoder: NSCoder) {
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func sizeToFit() {
override open func sizeToFit() {
super.sizeToFit()
self.frame = CGRect.init(x: self.frame.origin.x, y: self.frame.origin.y, width: 36, height: 13)
@@ -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,
@@ -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],
@@ -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)!,
@@ -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,
@@ -36,6 +36,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
@@ -23,7 +23,7 @@ import UIKit
extension SPApp {
public struct Badge {
struct Badge {
static var number: Int {
get {
@@ -27,21 +27,24 @@ extension SPApp {
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)
}
static var dateFirstLaunch: Date {
return ((UserDefaults.standard.object(forKey: "SPDateFirstLaunch") as? Date) ?? Date())
}
private init() {}
}
}
@@ -28,8 +28,8 @@ struct SPAppStore {
return "https://itunes.apple.com/by/app/id" + appID
}
static func open(appID: String) {
if let url = URL(string: "itms-apps://itunes.apple.com/app/id\(appID)"),
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)
@@ -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")
}
}
@@ -22,7 +22,7 @@
import UIKit
import AVFoundation
public class SPAudioPlayer: NSObject, AVAudioPlayerDelegate {
class SPAudioPlayer: NSObject, AVAudioPlayerDelegate {
fileprivate var player: AVAudioPlayer = AVAudioPlayer()
fileprivate var endPlayingComplection: (()->())? = nil
@@ -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?()
}
}
@@ -23,9 +23,9 @@ import UIKit
extension SPCodeDraw {
public class AudioIconPack : NSObject {
class AudioIconPack : NSObject {
@objc dynamic public class func drawPlay(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
@objc dynamic class func drawPlay(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
@@ -53,7 +53,7 @@ extension SPCodeDraw {
context.restoreGState()
}
@objc dynamic public class func drawPause(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
@objc dynamic class func drawPause(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
@@ -90,7 +90,7 @@ extension SPCodeDraw {
context.restoreGState()
}
@objc dynamic public class func drawStop(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
@objc dynamic class func drawStop(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
@@ -118,13 +118,13 @@ extension SPCodeDraw {
}
@objc(StyleKitNameResizingBehavior)
public enum ResizingBehavior: Int {
enum ResizingBehavior: Int {
case aspectFit /// The content is proportionally resized to fit into the target rectangle.
case aspectFill /// The content is proportionally resized to completely fill the target rectangle.
case stretch /// The content is stretched to match the entire target rectangle.
case center /// The content is centered in the target rectangle, but it is NOT resized.
public func apply(rect: CGRect, target: CGRect) -> CGRect {
func apply(rect: CGRect, target: CGRect) -> CGRect {
if rect == target || target == CGRect.zero {
return rect
}
@@ -21,7 +21,4 @@
import UIKit
struct SPCodeDraw {
private init() {}
}
struct SPCodeDraw { private init() {} }
@@ -23,9 +23,9 @@ import UIKit
extension SPCodeDraw {
public class SocialIconPack : NSObject {
class SocialIconPack : NSObject {
@objc dynamic public class func drawInstagram(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
@objc dynamic class func drawInstagram(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
@@ -115,7 +115,7 @@ extension SPCodeDraw {
}
@objc dynamic public class func drawVK(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
@objc dynamic class func drawVK(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
//// General Declarations
let context = UIGraphicsGetCurrentContext()!
@@ -167,7 +167,7 @@ extension SPCodeDraw {
context.restoreGState()
}
@objc dynamic public class func drawWhatsapp(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
@objc dynamic class func drawWhatsapp(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
//// General Declarations
let context = UIGraphicsGetCurrentContext()!
@@ -237,7 +237,7 @@ extension SPCodeDraw {
}
@objc dynamic public class func drawTelegram(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
@objc dynamic class func drawTelegram(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
//// General Declarations
let context = UIGraphicsGetCurrentContext()!
@@ -313,7 +313,7 @@ extension SPCodeDraw {
}
@objc dynamic public class func drawFacebook(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 41, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
@objc dynamic class func drawFacebook(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 41, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
//// General Declarations
let context = UIGraphicsGetCurrentContext()!
@@ -352,7 +352,7 @@ extension SPCodeDraw {
}
@objc dynamic public class func drawViber(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 41, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
@objc dynamic class func drawViber(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 41, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
//// General Declarations
let context = UIGraphicsGetCurrentContext()!
@@ -443,13 +443,13 @@ extension SPCodeDraw {
}
@objc(SocialIconStyleKitResizingBehavior)
public enum ResizingBehavior: Int {
enum ResizingBehavior: Int {
case aspectFit /// The content is proportionally resized to fit into the target rectangle.
case aspectFill /// The content is proportionally resized to completely fill the target rectangle.
case stretch /// The content is stretched to match the entire target rectangle.
case center /// The content is centered in the target rectangle, but it is NOT resized.
public func apply(rect: CGRect, target: CGRect) -> CGRect {
func apply(rect: CGRect, target: CGRect) -> CGRect {
if rect == target || target == CGRect.zero {
return rect
}
@@ -23,15 +23,15 @@ import UIKit
extension SPCodeDraw {
public class SystemIconPack : NSObject {
class SystemIconPack : NSObject {
private struct Cache {
static let gradient: CGGradient = CGGradient(colorsSpace: nil, colors: [UIColor.red.cgColor, UIColor.red.cgColor] as CFArray, locations: [0, 1])!
}
@objc dynamic public class var gradient: CGGradient { return Cache.gradient }
@objc dynamic class var gradient: CGGradient { return Cache.gradient }
@objc dynamic public class func drawFavorite(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
@objc dynamic class func drawFavorite(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
@@ -80,7 +80,7 @@ extension SPCodeDraw {
context.restoreGState()
}
@objc dynamic public class func drawFavoriteFill(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
@objc dynamic class func drawFavoriteFill(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
@@ -113,7 +113,7 @@ extension SPCodeDraw {
context.restoreGState()
}
@objc dynamic public class func drawShare(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
@objc dynamic class func drawShare(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
@@ -154,7 +154,7 @@ extension SPCodeDraw {
context.restoreGState()
}
@objc dynamic public class func drawClose(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
@objc dynamic class func drawClose(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
@@ -192,13 +192,13 @@ extension SPCodeDraw {
}
@objc(StyleKitNameResizingBehavior)
public enum ResizingBehavior: Int {
enum ResizingBehavior: Int {
case aspectFit /// The content is proportionally resized to fit into the target rectangle.
case aspectFill /// The content is proportionally resized to completely fill the target rectangle.
case stretch /// The content is stretched to match the entire target rectangle.
case center /// The content is centered in the target rectangle, but it is NOT resized.
public func apply(rect: CGRect, target: CGRect) -> CGRect {
func apply(rect: CGRect, target: CGRect) -> CGRect {
if rect == target || target == CGRect.zero {
return rect
}
@@ -21,7 +21,7 @@
import UIKit
public struct SPConstraints {
struct SPConstraints {
static func setEqualSizeSuperview(for view: UIView) {
if let superView = view.superview {
@@ -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()
@@ -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
}
}
@@ -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)
}
}
@@ -35,4 +35,8 @@ extension Date {
let date = formatter.date(from: value)
return date
}
func add(days: Int) -> Date {
return Calendar.current.date(byAdding: .day, value: days, to: self) ?? self
}
}
@@ -23,13 +23,13 @@ import Foundation
extension Strideable {
public mutating func setIfMore(when value: Self) {
mutating func setIfMore(when value: Self) {
if self > value {
self = value
}
}
public mutating func setIfFewer(when value: Self) {
mutating func setIfFewer(when value: Self) {
if self < value {
self = value
}
@@ -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])
}
}
}
}
@@ -32,20 +32,19 @@ 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, message: String, buttonTitle: String, cancelButtonTitle: String? = nil, complection: @escaping ()->() = {}, on viewController: UIViewController) {
let ac = UIAlertController(
title: title,
message: message,
preferredStyle: .alert
)
if cancelButtonTitle != nil {
ac.addAction(UIAlertAction.init(
title: cancelButtonTitle!,
style: UIAlertAction.Style.cancel,
handler: nil)
)
}
guard cancelButtonTitle != nil else { return }
ac.addAction(UIAlertAction.init(
title: cancelButtonTitle!,
style: UIAlertAction.Style.cancel,
handler: nil)
)
ac.addAction(UIAlertAction.init(
title: buttonTitle,
@@ -57,17 +56,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, 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 +73,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 +96,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)
}
}
@@ -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()
@@ -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()
}
}
}
@@ -21,7 +21,7 @@
import UIKit
public extension UIColor {
extension UIColor {
convenience init(hex: String) {
var red: CGFloat = 0.0
@@ -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
}
}
@@ -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)
}
}
@@ -23,13 +23,13 @@ import UIKit
extension UIImageView {
public func setNative() {
func setNative() {
self.layer.borderWidth = 0.5
self.layer.borderColor = SPNativeColors.midGray.cgColor
self.layer.masksToBounds = true
}
public func downloadedFrom(url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit, withComplection complection: @escaping (UIImage?) -> () = {_ in }) {
func downloadedFrom(url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit, withComplection complection: @escaping (UIImage?) -> () = {_ in }) {
DispatchQueue.main.async {
self.contentMode = mode
}
@@ -47,7 +47,7 @@ extension UIImageView {
}.resume()
}
public func downloadedFrom(link: String, contentMode mode: UIView.ContentMode = .scaleAspectFit, withComplection complection: @escaping (UIImage?) -> () = {_ in }) {
func downloadedFrom(link: String, contentMode mode: UIView.ContentMode = .scaleAspectFit, withComplection complection: @escaping (UIImage?) -> () = {_ in }) {
guard let url = URL(string: link) else { return }
downloadedFrom(url: url, contentMode: mode, withComplection: complection)
}
@@ -21,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 }
@@ -71,7 +71,23 @@ public extension UILabel {
attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attributedString.length))
self.attributedText = attributedString
}
func setFormat(text: String, positions: [(start: Int, length: Int)], hithiglightFont: UIFont, higlightColor: UIColor) {
let title = NSMutableAttributedString.init(string: text)
for position in positions {
title.addAttributes(
[
NSAttributedString.Key.foregroundColor : higlightColor,
NSAttributedString.Key.font : hithiglightFont
],
range: NSRange.init(location: position.start, length: position.length)
)
}
self.attributedText = title
}
}
@@ -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)
}
@@ -32,7 +32,7 @@ extension UIViewController {
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
@@ -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,6 +135,10 @@ extension UIViewController {
if #available(iOS 11.0, *) {
self.navigationItem.largeTitleDisplayMode = .never
}
case .noContent:
if #available(iOS 11.0, *) {
self.navigationItem.largeTitleDisplayMode = .never
}
}
}
}
@@ -21,18 +21,18 @@
import UIKit
public extension UIView {
extension UIView {
var viewController: UIViewController? {
var controller: UIViewController? {
get {
if let nextResponder = self.next as? UIViewController { return nextResponder }
else if let nextResponder = self.next as? UIView { return nextResponder.viewController }
else if let nextResponder = self.next as? UIView { return nextResponder.controller }
else { return nil }
}
}
}
public extension UIView {
extension UIView {
var safeArea: UIEdgeInsets {
if #available(iOS 11.0, *) {
@@ -42,24 +42,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 +62,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 +95,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 +131,7 @@ public extension UIView {
}
}
public extension UIView {
extension UIView {
func addGrade(alpha: CGFloat, color: UIColor = UIColor.black) -> UIView {
let gradeView = UIView.init()
@@ -174,14 +161,14 @@ extension UIView {
let xTranslation = (self.frame.width - shadowWidth) / 2 + (self.frame.width * xTranslationFactor)
let yTranslation = (self.frame.height - shadowHeight) / 2 + (self.frame.height * yTranslationFactor)
let cornerRadius = self.frame.minSideSize * cornerRadiusFactor
let cornerRadius = self.frame.minSide * cornerRadiusFactor
let shadowPath = UIBezierPath.init(
roundedRect: CGRect.init(x: xTranslation, y: yTranslation, width: shadowWidth, height: shadowHeight),
cornerRadius: cornerRadius
)
let blurRadius = self.frame.minSideSize * blurRadiusFactor
let blurRadius = self.frame.minSide * blurRadiusFactor
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize.zero
@@ -277,6 +264,6 @@ extension UIView {
}
func round() {
self.layer.cornerRadius = self.frame.minSideSize / 2
self.layer.cornerRadius = self.frame.minSide / 2
}
}
@@ -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,35 @@ 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: [], 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 countSymbol: String {
return "%u"
}
private init() {}
}
@@ -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 {
@@ -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)
@@ -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) {
@@ -46,6 +46,30 @@ class SPTelegram {
SPApp.open(link: url, redirect: true)
}
static func joinChat(id: String) {
let openInBrowser = {
let url = "https://t.me/joinchat/\(id)"
SPApp.open(link: url, redirect: true)
}
if SPTelegram.isSetApp {
let urlStringEncoded = id.addingPercentEncoding( withAllowedCharacters: .urlHostAllowed)
let urlOptional = URL(string: "tg://join?invite=\(urlStringEncoded ?? "")")
if let url = urlOptional {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
} else {
openInBrowser()
}
} else {
openInBrowser()
}
} else {
openInBrowser()
}
}
static func openBot(username: String) {
var username = username
if username.first == "@" {
@@ -59,5 +83,5 @@ class SPTelegram {
}
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,62 +0,0 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Foundation
import CoreSpotlight
import MobileCoreServices
struct SPSpotlight {
static let domainIdentifier = "by.ivanvorobei"
static func addItem(identifier: String, title: String, description: String, addedData: Date? = nil, keywords: [String] = []) {
if #available(iOS 9.0, *) {
let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeMessage as String)
attributeSet.title = title
attributeSet.contentDescription = description
attributeSet.keywords = keywords
if addedData != nil {
attributeSet.contentCreationDate = addedData!
}
let item = CSSearchableItem(uniqueIdentifier: "\(identifier)", domainIdentifier: SPSpotlight.domainIdentifier, attributeSet: attributeSet)
CSSearchableIndex.default().indexSearchableItems([item]) { error in
if let error = error {
print("SPSpotlight addItem error: \(error.localizedDescription)")
}
}
}
}
static func removeItem(identifier: String) {
if #available(iOS 9.0, *) {
CSSearchableIndex.default().deleteSearchableItems(withIdentifiers: ["\(identifier)"]) { error in
if let error = error {
print("SPSpotlight removeItem error: \(error.localizedDescription)")
}
}
}
}
private init() {}
}
@@ -21,19 +21,19 @@
import UIKit
public enum SPStatusBar {
enum SPStatusBar {
case dark
case light
}
public enum SPSystemIconType {
enum SPSystemIcon {
case share
case close
case favorite
case favorite_fill
}
public enum SPSocialNetwork {
enum SPSocialNetwork {
case whatsapp
case telegram
case vk
@@ -41,7 +41,7 @@ public enum SPSocialNetwork {
case viber
}
public enum SPOauthState {
enum SPOauthState {
case succsess
case unvalidLogin
case invalidLogin
@@ -51,25 +51,26 @@ public enum SPOauthState {
case error
}
public enum SPSeparatorInsetStyle {
enum SPSeparatorInsetStyle {
case beforeImage
case all
case none
case auto
}
public enum SPNavigationTitleStyle {
enum SPNavigationTitleStyle {
case large
case small
case stork
case noContent
}
public enum SPSystemApp {
enum SPSystemApp {
case photos
case setting
}
public enum SPSelectionType {
enum SPSelectionType {
case select
case unselect
}
@@ -32,28 +32,28 @@ class SPAppStoreActionButton: SPDownloadingButton {
switch self.style {
case .base:
self.backgroundColor = self.secondColor
self.titleLabel?.font = UIFont.system(type: .Bold, size: 14)
self.titleLabel?.font = UIFont.system(weight: .bold, size: 14)
self.contentEdgeInsets = UIEdgeInsets.init(top: 6, left: 15, bottom: 6, right: 15)
break
case .main:
self.backgroundColor = self.baseColor
self.layer.borderWidth = 0
self.setTitleColor(UIColor.white)
self.titleLabel?.font = UIFont.system(type: .Bold, size: 14)
self.titleLabel?.font = UIFont.system(weight: .bold, size: 14)
self.contentEdgeInsets = UIEdgeInsets.init(top: 6, left: 15, bottom: 6, right: 15)
break
case .buyInStore:
self.backgroundColor = self.baseColor
self.layer.borderWidth = 0
self.setTitleColor(UIColor.white)
self.titleLabel?.font = UIFont.system(type: .Bold, size: 14)
self.titleLabel?.font = UIFont.system(weight: .bold, size: 14)
self.contentEdgeInsets = UIEdgeInsets.init(top: 8, left: 15, bottom: 8, right: 15)
break
case .line:
self.backgroundColor = UIColor.clear
self.layer.borderWidth = 1
self.layer.borderColor = self.baseColor.cgColor
self.titleLabel?.font = UIFont.system(type: .Medium, size: 14)
self.titleLabel?.font = UIFont.system(weight: .medium, size: 14)
self.contentEdgeInsets = UIEdgeInsets.init(top: 6, left: 15, bottom: 6, right: 15)
break
}
@@ -44,7 +44,7 @@ class SPAppleMusicButton: SPButton {
override func commonInit() {
super.commonInit()
self.layer.cornerRadius = 8
self.titleLabel?.font = UIFont.system(type: .DemiBold, size: 15)
self.titleLabel?.font = UIFont.system(weight: .demiBold, size: 15)
self.contentEdgeInsets = UIEdgeInsets.init(top: 12, left: 27, bottom: 12, right: 27)
self.type = .unselect
}
@@ -64,7 +64,7 @@ class SPAppleMusicSectionButtonsView: SPView {
func layout(origin: CGPoint, width: CGFloat) {
self.frame.origin = origin
self.set(width: width, height: sectionHeight)
self.frame.set(width: width, height: sectionHeight)
self.layoutSubviews()
}
@@ -72,22 +72,22 @@ class SPAppleMusicSectionButtonsView: SPView {
super.layoutSubviews()
self.topSeparatorView.frame.origin = .zero
self.topSeparatorView.setWidth(self.frame.width)
self.topSeparatorView.frame.set(width: self.frame.width)
self.bottomSeparatorView.frame.origin.x = 0
self.bottomSeparatorView.frame.bottomYPosition = self.frame.height
self.bottomSeparatorView.setWidth(self.frame.width)
self.bottomSeparatorView.frame.bottomY = self.frame.height
self.bottomSeparatorView.frame.set(width: self.frame.width)
let buttonWidth = (self.frame.width - self.buttonsSpace) / 2
self.leftButton.sizeToFit()
self.leftButton.setWidth(buttonWidth)
self.leftButton.frame.set(width: buttonWidth)
self.leftButton.frame.origin.x = 0
self.leftButton.center.y = self.frame.height / 2
self.rightButton.sizeToFit()
self.rightButton.setWidth(buttonWidth)
self.rightButton.frame.bottomXPosition = self.frame.width
self.rightButton.frame.set(width: buttonWidth)
self.rightButton.frame.bottomX = self.frame.width
self.rightButton.center.y = self.frame.height / 2
}
}
@@ -21,20 +21,43 @@
import UIKit
public class SPButton: UIButton {
class SPButton: UIButton {
override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
if self.title(for: .normal) != nil {
let inset: CGFloat = 6
let sideSize = self.frame.height - inset * 2
let titleFrame = self.titleRect(forContentRect: contentRect)
return CGRect.init(x: titleFrame.origin.x - sideSize - 6, y: 0, width: sideSize, height: self.frame.height)
} else {
return super.imageRect(forContentRect: contentRect)
}
}
override var isHighlighted: Bool {
didSet {
if self.isHighlighted {
self.imageView?.alpha = 0.7
} else {
self.imageView?.alpha = 1
}
}
}
var gradientView: SPGradientView? {
didSet {
self.gradientView?.isUserInteractionEnabled = false
if self.gradientView?.superview == nil {
if self.gradientView != nil {
self.insertSubview(self.gradientView!, at: 0)
if self.imageView != nil {
self.insertSubview(self.gradientView!, belowSubview: self.imageView!)
}
}
}
}
}
var round: Bool = false {
var rounded: Bool = false {
didSet {
self.layoutSubviews()
}
@@ -50,13 +73,26 @@ public class SPButton: UIButton {
self.commonInit()
}
internal func commonInit() {}
internal func commonInit() {
self.adjustsImageWhenHighlighted = false
}
override public func layoutSubviews() {
override func layoutSubviews() {
super.layoutSubviews()
self.gradientView?.setEqualsBoundsFromSuperview()
if self.round {
self.gradientView?.setSuperviewBounds()
if self.rounded {
self.round()
}
}
func set(enable: Bool, animatable: Bool) {
self.isEnabled = enable
if animatable {
SPAnimation.animate(0.3, animations: {
self.alpha = enable ? 1 : 0.6
})
} else {
self.alpha = enable ? 1 : 0.6
}
}
}
@@ -0,0 +1,52 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
class SPCircleCloseButton: SPSystemIconButton {
var side: CGFloat = 30
override init() {
super.init(type: .close)
self.rounded = true
self.backgroundColor = SPNativeColors.customGray
self.color = SPNativeColors.gray
self.widthIconFactor = 0.36
self.heightIconFactor = 0.36
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func layout(bottomX: CGFloat, y: CGFloat) {
self.sizeToFit()
self.frame.bottomX = bottomX
self.frame.origin.y = y
}
override func sizeToFit() {
super.sizeToFit()
self.frame.set(width: self.side)
self.frame.set(height: self.side)
}
}
@@ -71,8 +71,8 @@ class SPDotButton: SPButton {
override func sizeToFit() {
super.sizeToFit()
self.setWidth(self.customSideSize)
self.setHeight(self.customSideSize)
self.frame.set(width: self.customSideSize)
self.frame.set(height: self.customSideSize)
self.layoutSubviews()
}
@@ -87,9 +87,9 @@ class SPDotButton: SPButton {
var currentXPosition: CGFloat = insest
for dotView in self.dotsView {
dotView.setWidth(sideSize)
dotView.setHeight(sideSize)
dotView.setYCenteringFromSuperview()
dotView.frame.set(width: sideSize)
dotView.frame.set(height: sideSize)
dotView.setYCenter()
dotView.frame.origin.x = currentXPosition
dotView.round()
currentXPosition += (sideSize + space)
@@ -24,7 +24,6 @@ import UIKit
class SPDownloadingButton: SPButton {
let activityIndicatorView = UIActivityIndicatorView.init()
var isFrameRounded: Bool = false
func startLoading() {
self.activityIndicatorView.alpha = 0
@@ -39,7 +38,10 @@ class SPDownloadingButton: SPButton {
})
}
func stopLoading() {
func stopLoading(newText: String? = nil) {
if let newText = newText {
self.setTitle(newText)
}
SPAnimation.animate(0.2, animations: {
self.activityIndicatorView.alpha = 0
}, withComplection: {
@@ -53,10 +55,6 @@ class SPDownloadingButton: SPButton {
override func layoutSubviews() {
super.layoutSubviews()
self.activityIndicatorView.center = CGPoint.init(x: self.frame.width / 2, y: self.frame.height / 2)
if self.isFrameRounded {
self.round()
}
}
}
@@ -44,7 +44,7 @@ class SPNativeLargeButton: SPDownloadingButton {
override func commonInit() {
super.commonInit()
self.titleLabel?.font = UIFont.system(type: UIFont.BoldType.DemiBold, size: 16)
self.titleLabel?.font = UIFont.system(weight: UIFont.FontWeight.demiBold, size: 16)
self.setTitleColor(UIColor.white)
self.backgroundColor = SPNativeColors.blue
self.layer.masksToBounds = true
@@ -58,13 +58,13 @@ class SPNativeLargeButton: SPDownloadingButton {
let sideSpace: CGFloat = superview.frame.width * 0.112
var width = superview.frame.width - sideSpace * 2
width.setIfMore(when: 335)
self.setWidth(width)
self.frame.set(width: width)
}
}
override func layoutSubviews() {
super.layoutSubviews()
self.gradientView?.setEqualsBoundsFromSuperview()
self.gradientView?.setSuperviewBounds()
self.gradientView?.layer.cornerRadius = self.layer.cornerRadius
self.gradientView?.gradientLayer.cornerRadius = self.layer.cornerRadius
}
@@ -62,7 +62,7 @@ class SPPlayCircleButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
self.iconView.setEqualsFrameFromBounds(self, withWidthFactor: 0.45, withHeightFactor: 0.45, withCentering: true)
self.iconView.setBounds(self, withWidthFactor: 0.45, withHeightFactor: 0.45, withCentering: true)
self.round()
}
@@ -81,7 +81,7 @@ class SPSocialButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
self.iconView.setEqualsFrameFromBounds(self, withWidthFactor: self.widthIconFactor, withHeightFactor: self.heightIconFactor, withCentering: true)
self.iconView.setBounds(self, withWidthFactor: self.widthIconFactor, withHeightFactor: self.heightIconFactor, withCentering: true)
self.round()
}
}
@@ -21,15 +21,16 @@
import UIKit
class SPSystemIconButton: UIButton {
class SPSystemIconButton: SPButton {
let iconView = SPSystemIconView.init()
var widthIconFactor: CGFloat = 1
var heightIconFactor: CGFloat = 1
var type: SPSystemIconType {
var icon: SPSystemIcon {
didSet {
self.iconView.type = self.type
self.iconView.icon = self.icon
}
}
@@ -49,17 +50,17 @@ class SPSystemIconButton: UIButton {
}
}
init() {
self.type = .share
super.init(frame: CGRect.zero)
override init() {
self.icon = .share
super.init()
self.commonInit()
}
init(type: SPSystemIconType) {
self.type = type
super.init(frame: CGRect.zero)
self.iconView.type = self.type
self.type = type
init(type: SPSystemIcon) {
self.icon = type
super.init()
self.iconView.icon = self.icon
self.icon = type
self.commonInit()
}
@@ -67,13 +68,14 @@ class SPSystemIconButton: UIButton {
fatalError("init(coder:) has not been implemented")
}
fileprivate func commonInit() {
override func commonInit() {
super.commonInit()
self.iconView.isUserInteractionEnabled = false
self.addSubview(self.iconView)
}
override func layoutSubviews() {
super.layoutSubviews()
self.iconView.setEqualsFrameFromBounds(self, withWidthFactor: self.widthIconFactor, withHeightFactor: self.heightIconFactor, withCentering: true)
self.iconView.setBounds(self, withWidthFactor: self.widthIconFactor, withHeightFactor: self.heightIconFactor, withCentering: true)
}
}
@@ -103,10 +103,13 @@ class SPGolubevIconView: UIView {
case .headphones:
SPCodeDraw.GolubevIconPack.drawHeadphones(frame: rect, resizing: .aspectFit, white: self.whiteColor, light: self.lightColor, medium: self.mediumColor, dark: self.darkColor)
break
case .windmill:
SPCodeDraw.GolubevIconPack.drawWindmill(frame: rect, resizing: .aspectFit, white: self.whiteColor, light: self.lightColor, medium: self.mediumColor, dark: self.darkColor)
break
}
}
public enum IconType {
enum IconType {
case camera
case photoLibrary
case ball
@@ -116,6 +119,7 @@ class SPGolubevIconView: UIView {
case documents
case compass
case headphones
case windmill
}
}
@@ -23,7 +23,7 @@ import UIKit
class SPSystemIconView: UIView {
var type: SPSystemIconType {
var icon: SPSystemIcon {
didSet {
self.setNeedsDisplay()
}
@@ -36,13 +36,13 @@ class SPSystemIconView: UIView {
}
init() {
self.type = .share
self.icon = .share
super.init(frame: CGRect.zero)
self.commonInit()
}
init(type: SPSystemIconType) {
self.type = type
init(type: SPSystemIcon) {
self.icon = type
super.init(frame: CGRect.zero)
self.commonInit()
}
@@ -57,7 +57,7 @@ class SPSystemIconView: UIView {
override func draw(_ rect: CGRect) {
super.draw(rect)
switch type {
switch icon {
case .share:
SPCodeDraw.SystemIconPack.drawShare(frame: rect, resizing: .aspectFit, color: self.color)
break
@@ -21,7 +21,7 @@
import UIKit
public class SPCollectionViewCell: UICollectionViewCell {
class SPCollectionViewCell: UICollectionViewCell {
var currentIndexPath: IndexPath?
@@ -37,7 +37,7 @@ public class SPCollectionViewCell: UICollectionViewCell {
internal func commonInit() {}
public override func prepareForReuse() {
override func prepareForReuse() {
super.prepareForReuse()
self.currentIndexPath = nil
}
@@ -0,0 +1,53 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
class SPImageCollectionViewCell: SPCollectionViewCell {
let imageView = SPDownloadingImageView()
override func commonInit() {
super.commonInit()
self.addSubview(self.imageView)
SPConstraints.setEqualSizeSuperview(for: self.imageView)
self.configure()
}
private func configure() {
self.currentIndexPath = nil
self.imageView.removeImage()
self.imageView.contentMode = .scaleAspectFill
self.imageView.layer.masksToBounds = true
}
override func prepareForReuse() {
super.prepareForReuse()
self.configure()
}
override func layoutSubviews() {
super.layoutSubviews()
self.imageView.layer.cornerRadius = 12
}
}
@@ -86,8 +86,8 @@ class SPMengTransformCollectionViewCell: SPCollectionViewCell {
self.backgroundImageView.bottomAnchor.constraint(equalTo:
contentView.bottomAnchor, constant: 0).isActive = true
self.gradientView.startColorPosition = .TopLeft
self.gradientView.endColorPosition = .BottomRight
self.gradientView.startColorPosition = .topLeft
self.gradientView.endColorPosition = .bottomRight
self.gradientView.isHidden = true
self.gradientView.translatesAutoresizingMaskIntoConstraints = false
self.gradientView.layer.masksToBounds = false
@@ -125,7 +125,7 @@ class SPMengTransformCollectionViewCell: SPCollectionViewCell {
self.titleLabel.text = ""
self.titleLabel.setDeepShadowForLetters()
self.titleLabel.translatesAutoresizingMaskIntoConstraints = false
self.titleLabel.font = UIFont.system(type: .DemiBold, size: 32)
self.titleLabel.font = UIFont.system(weight: .demiBold, size: 32)
self.titleLabel.textColor = UIColor.white
self.titleLabel.numberOfLines = 0
contentView.addSubview(self.titleLabel)
@@ -140,7 +140,7 @@ class SPMengTransformCollectionViewCell: SPCollectionViewCell {
self.subtitleLabel.text = ""
self.subtitleLabel.setDeepShadowForLetters()
self.subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
self.subtitleLabel.font = UIFont.system(type: .Regular, size: 17)
self.subtitleLabel.font = UIFont.system(weight: .regular, size: 17)
self.subtitleLabel.textColor = UIColor.white
self.subtitleLabel.numberOfLines = 0
contentView.addSubview(self.subtitleLabel)
@@ -21,7 +21,7 @@
import UIKit
public class SPCollectionViewLayout: UICollectionViewFlowLayout {
class SPCollectionViewLayout: UICollectionViewFlowLayout {
var itemSpacingFactor: CGFloat = 0.11
var minItemSpace: CGFloat = 0
@@ -54,7 +54,7 @@ public class SPCollectionViewLayout: UICollectionViewFlowLayout {
}
}
required public init?(coder aDecoder: NSCoder) {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
@@ -65,7 +65,7 @@ public class SPCollectionViewLayout: UICollectionViewFlowLayout {
self.minimumLineSpacing = 0
}
public override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
if !self.isPaging {
return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
@@ -86,7 +86,7 @@ public class SPCollectionViewLayout: UICollectionViewFlowLayout {
} else {
proposedContentOffset.x = round(rawPageValue) * self.pageWidth
}
return proposedContentOffset;
return proposedContentOffset
case .vertical:
let rawPageValue = (self.collectionView!.contentOffset.y) / self.pageHeight
@@ -102,11 +102,13 @@ public class SPCollectionViewLayout: UICollectionViewFlowLayout {
} else {
proposedContentOffset.y = round(rawPageValue) * self.pageHeight
}
return proposedContentOffset;
return proposedContentOffset
default:
return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
}
}
override public func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let collectionView = self.collectionView,
let superAttributes = super.layoutAttributesForElements(in: rect) else {
return super.layoutAttributesForElements(in: rect)
@@ -155,11 +157,13 @@ public class SPCollectionViewLayout: UICollectionViewFlowLayout {
owner.alpha = alpha
}
}
default:
return super.layoutAttributesForElements(in: rect)
}
return newAttributesArray
}
override public func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
@@ -167,7 +171,7 @@ public class SPCollectionViewLayout: UICollectionViewFlowLayout {
var deleteIndexPaths: [IndexPath] = []
var insertIndexPaths: [IndexPath] = []
public override func prepare(forCollectionViewUpdates updateItems: [UICollectionViewUpdateItem]) {
override func prepare(forCollectionViewUpdates updateItems: [UICollectionViewUpdateItem]) {
super.prepare(forCollectionViewUpdates: updateItems)
for item in updateItems {
if item.updateAction == .delete {
@@ -184,13 +188,13 @@ public class SPCollectionViewLayout: UICollectionViewFlowLayout {
}
}
public override func finalizeCollectionViewUpdates() {
override func finalizeCollectionViewUpdates() {
super.finalizeCollectionViewUpdates()
self.insertIndexPaths.removeAll()
self.deleteIndexPaths.removeAll()
}
override public func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let attributes: UICollectionViewLayoutAttributes? = super.initialLayoutAttributesForAppearingItem(at: itemIndexPath)
@@ -205,7 +209,7 @@ public class SPCollectionViewLayout: UICollectionViewFlowLayout {
return attributes
}
override public func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
override func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let attributes: UICollectionViewLayoutAttributes? = super.finalLayoutAttributesForDisappearingItem(at: itemIndexPath)
@@ -218,7 +222,7 @@ public class SPCollectionViewLayout: UICollectionViewFlowLayout {
return attributes
}
override public func prepare() {
override func prepare() {
super.prepare()
guard let collectionView = self.collectionView else {
return
@@ -261,6 +265,8 @@ public class SPCollectionViewLayout: UICollectionViewFlowLayout {
self.minimumLineSpacing = collectionView.frame.width * itemSpacingFactor
case .vertical:
self.minimumLineSpacing = collectionView.frame.height * itemSpacingFactor
default:
break
}
self.minimumLineSpacing.setIfMore(when: self.maxItemSpace)
self.minimumLineSpacing.setIfFewer(when: self.minItemSpace)
@@ -21,42 +21,49 @@
import UIKit
public class SPCollectionView: UICollectionView {
class SPCollectionView: UICollectionView {
var layout = SPCollectionViewLayout()
let layout = UICollectionViewFlowLayout()
private var cacheImages: [(link: String, image: UIImage)] = []
required public init?(coder aDecoder: NSCoder) {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
init(frame: CGRect) {
super.init(frame: frame, collectionViewLayout: self.layout)
commonInit()
init() {
super.init(frame: .zero, collectionViewLayout: self.layout)
self.commonInit()
}
init() {
super.init(frame: CGRect.zero, collectionViewLayout: self.layout)
commonInit()
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: self.layout)
self.commonInit()
}
internal func commonInit() {
self.layout.scrollDirection = .vertical
self.backgroundColor = UIColor.clear
self.collectionViewLayout = self.layout
self.decelerationRate = UIScrollView.DecelerationRate.fast
self.delaysContentTouches = false
self.isPagingEnabled = false
self.showsHorizontalScrollIndicator = false
self.showsVerticalScrollIndicator = false
}
func height(rows: Int) -> CGFloat {
return self.layout.itemSize.height * CGFloat(rows) + self.layout.minimumLineSpacing * CGFloat(rows - 1)
}
func width(columns: Int) -> CGFloat {
return self.layout.itemSize.width * CGFloat(columns) + self.layout.minimumInteritemSpacing * CGFloat(columns - 1)
}
}
//MARK: - cache
//MARK: - Cache
extension SPCollectionView {
func setCachedImage(link: String, indexPath: IndexPath, on imageView: SPDownloadingImageView, cell: SPCollectionViewCell) {
cell.currentIndexPath = indexPath
if let image = self.fromCahce(link: link) {
imageView.setImage(image: image, animatable: false)
} else {
@@ -0,0 +1,46 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
class SPImagesCollectionView: SPCollectionView {
private let imageCellIdentificator: String = "imageCellIdentificator"
override func commonInit() {
super.commonInit()
self.layout.scrollDirection = .horizontal
self.register(SPImageCollectionViewCell.self, forCellWithReuseIdentifier: self.imageCellIdentificator)
}
func layout(y: CGFloat, cellSize: CGSize, space: CGFloat, lines: Int) {
self.layout.minimumLineSpacing = space
self.layout.minimumInteritemSpacing = self.layout.minimumLineSpacing
self.frame.set(width: self.superview?.frame.width ?? 0)
self.frame.set(width: self.superview?.frame.width ?? 0, height: cellSize.height * CGFloat(lines) + self.layout.minimumInteritemSpacing * CGFloat(lines - 1))
self.frame.origin = CGPoint.init(x: 0, y: y)
self.layout.itemSize = cellSize
}
func dequeueImageCell(indexPath: IndexPath) -> SPImageCollectionViewCell {
return self.dequeueReusableCell(withReuseIdentifier: self.imageCellIdentificator, for: indexPath) as! SPImageCollectionViewCell
}
}
@@ -40,7 +40,7 @@ struct SPMengTransformCollectionData {
}
}
class SPMengTransformCollectionView: SPCollectionView {
class SPMengTransformCollectionView: SPPageCollectionView {
var data: [SPMengTransformCollectionData] = []
var withParalax: Bool = true
@@ -140,7 +140,7 @@ extension SPMengTransformCollectionView: UICollectionViewDataSource {
cell.backgroundImageView.setImage(image: image, animatable: false)
showGrade(false)
} else {
cell.backgroundImageView.activityIndiactorView.startAnimating()
cell.backgroundImageView.removeImage()
cell.titleLabel.alpha = 0
cell.subtitleLabel.alpha = 0
SPDownloader.image(link: link) { (response) in
@@ -0,0 +1,92 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
class SPPageCollectionView: UICollectionView {
var layout = SPCollectionViewLayout()
private var cacheImages: [(link: String, image: UIImage)] = []
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
init(frame: CGRect) {
super.init(frame: frame, collectionViewLayout: self.layout)
commonInit()
}
init() {
super.init(frame: CGRect.zero, collectionViewLayout: self.layout)
commonInit()
}
internal func commonInit() {
self.layout.scrollDirection = .vertical
self.backgroundColor = UIColor.clear
self.collectionViewLayout = self.layout
self.decelerationRate = UIScrollView.DecelerationRate.fast
self.delaysContentTouches = false
self.isPagingEnabled = false
self.showsHorizontalScrollIndicator = false
self.showsVerticalScrollIndicator = false
}
}
//MARK: - cache
extension SPPageCollectionView {
func setCachedImage(link: String, indexPath: IndexPath, on imageView: SPDownloadingImageView, cell: SPCollectionViewCell) {
if let image = self.fromCahce(link: link) {
imageView.setImage(image: image, animatable: false)
} else {
SPDownloader.image(link: link) { (response) in
if let image = response {
if cell.currentIndexPath == indexPath {
imageView.setImage(image: image, animatable: true)
self.toCache(link: link, image: image)
}
}
}
}
}
func toCache(link: String, image: UIImage?) {
if image == nil {
return
}
if self.fromCahce(link: link) == nil {
self.cacheImages.append((link: link, image: image!))
}
}
func fromCahce(link: String) -> UIImage? {
let cachedData = self.cacheImages.first(where: {
$0.link == link
})
return cachedData?.image
}
}
@@ -21,30 +21,28 @@
import UIKit
public class SPController: SPStatusBarManagerController {
class SPController: SPStatusBarManagerController {
let emptyTitlesView = SPEmptyTitlesView(title: "No Data", subtitle: "No data or information")
let emptyTitlesView = SPEmptyLabelsView(title: "No Data", subtitle: "No data or information")
override public func viewDidLoad() {
override func viewDidLoad() {
super.viewDidLoad()
self.emptyTitlesView.isHidden = true
self.view.addSubview(self.emptyTitlesView)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.updateLayout(with: self.view.frame.size)
}
override public func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { (contex) in
self.updateLayout(with: size)
}, completion: nil)
}
@available(iOS 11.0, *)
override public func viewLayoutMarginsDidChange() {
super.viewLayoutMarginsDidChange()
self.updateLayout(with: self.view.frame.size)
}
func updateLayout(with size: CGSize) {
self.emptyTitlesView.layout(centerY: size.height / 2)
}
@@ -77,8 +77,6 @@ class SPNativeTableController: SPTableController {
self.activityIndicatorView.stopAnimating()
self.activityIndicatorView.color = SPNativeColors.gray
self.view.addSubview(self.activityIndicatorView)
self.updateLayout(with: self.view.frame.size)
}
override func numberOfSections(in tableView: UITableView) -> Int {
@@ -31,6 +31,10 @@ class SPProposeController: SPController {
return 0.5
}
private var gradeFactor: CGFloat {
return 0.6
}
private var space: CGFloat {
return 6
}
@@ -75,8 +79,6 @@ class SPProposeController: SPController {
if let image = self.data.image {
self.areaView.imageView.setImage(image: image, animatable: false)
}
self.updateLayout(with: self.view.frame.size)
}
override func viewDidAppear(_ animated: Bool) {
@@ -88,11 +90,11 @@ class SPProposeController: SPController {
}
private func present() {
SPVibration.impact(system: .warning)
SPVibration.impact(.warning)
self.areaView.frame.origin.y = self.view.frame.size.height
self.areaView.isHidden = false
SPAnimationSpring.animate(self.animationDuration, animations: {
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.6)
self.view.backgroundColor = UIColor.black.withAlphaComponent(self.gradeFactor)
self.areaView.frame.origin.y = self.view.frame.size.height - self.areaView.frame.height - (self.bottomSafeArea / 2) - self.space
}, spring: 1,
velocity: 1,
@@ -100,7 +102,6 @@ class SPProposeController: SPController {
}
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
let hide = {
self.view.backgroundColor = UIColor.black.withAlphaComponent(0)
self.areaView.frame.origin.y = self.view.frame.size.height
@@ -128,10 +129,8 @@ class SPProposeController: SPController {
}
override func updateLayout(with size: CGSize) {
self.areaView.setWidth(size.width - (self.space * 2))
self.areaView.layoutSubviews()
self.areaView.sizeToFit()
self.areaView.frame.origin.x = self.space
super.updateLayout(with: size)
self.areaView.layout(origin: CGPoint.init(x: self.space, y: 0), width: size.width - (self.space * 2))
self.areaView.frame.origin.y = self.view.frame.size.height - self.areaView.frame.height - (self.bottomSafeArea / 2) - self.space
}
@@ -140,6 +139,7 @@ class SPProposeController: SPController {
let returnAreaViewToPoint = {
SPAnimationSpring.animate(self.animationDuration, animations: {
self.areaView.frame.origin.y = self.view.frame.size.height - self.areaView.frame.height - (self.bottomSafeArea / 2) - self.space
self.view.backgroundColor = UIColor.black.withAlphaComponent(self.gradeFactor)
}, spring: 1,
velocity: 1,
options: .transitionCurlDown,
@@ -156,6 +156,13 @@ class SPProposeController: SPController {
let translation = sender.translation(in: self.view)
self.areaView.center = CGPoint(x: areaView.center.x + 0, y: areaView.center.y + translation.y / 4)
sender.setTranslation(CGPoint.zero, in: self.view)
let baseY = self.view.frame.size.height - self.areaView.frame.height - (self.bottomSafeArea / 2) - self.space
let dem = -(baseY - self.areaView.frame.origin.y) / 6
var newGrade = self.gradeFactor - (dem / 100)
newGrade.setIfFewer(when: 0.2)
newGrade.setIfMore(when: self.gradeFactor + 0.05)
self.view.backgroundColor = UIColor.black.withAlphaComponent(newGrade)
case .ended:
returnAreaViewToPoint()
default:
@@ -179,10 +186,9 @@ class SPProposeController: SPController {
let subtitleLabel = UILabel()
let imageView = SPDownloadingImageView()
let button = SPNativeLargeButton()
let closeButton = SPSystemIconButton(type: SPSystemIconType.close)
let closeButton = SPSystemIconButton(type: SPSystemIcon.close)
var imageSideSize: CGFloat = 160
private let space: CGFloat = 36
init() {
@@ -191,18 +197,18 @@ class SPProposeController: SPController {
self.layer.masksToBounds = true
self.layer.cornerRadius = 34
self.titleLabel.font = UIFont.system(type: .Regular, size: 28)
self.titleLabel.font = UIFont.system(weight: .regular, size: 28)
self.titleLabel.textColor = UIColor.init(hex: "939393")
self.titleLabel.numberOfLines = 1
self.titleLabel.adjustsFontSizeToFitWidth = true
self.titleLabel.minimumScaleFactor = 0.5
self.titleLabel.setCenteringAlignment()
self.titleLabel.setCenterAlignment()
self.addSubview(self.titleLabel)
self.subtitleLabel.font = UIFont.system(type: .Regular, size: 16)
self.subtitleLabel.font = UIFont.system(weight: .regular, size: 16)
self.subtitleLabel.textColor = SPNativeColors.black
self.subtitleLabel.numberOfLines = 0
self.subtitleLabel.setCenteringAlignment()
self.subtitleLabel.setCenterAlignment()
self.addSubview(self.subtitleLabel)
self.imageView.gradeView.backgroundColor = UIColor.white
@@ -210,15 +216,16 @@ class SPProposeController: SPController {
self.imageView.layer.masksToBounds = true
self.addSubview(self.imageView)
self.button.titleLabel?.font = UIFont.system(type: UIFont.BoldType.Medium, size: 15)
self.button.titleLabel?.font = UIFont.system(weight: UIFont.FontWeight.medium, size: 15)
self.button.setTitleColor(SPNativeColors.black)
self.button.backgroundColor = UIColor.init(hex: "D4D3DB")
self.button.layer.cornerRadius = 13
self.button.backgroundColor = SPNativeColors.lightGray
self.addSubview(self.button)
self.closeButton.widthIconFactor = 0.4
self.closeButton.heightIconFactor = 0.4
self.closeButton.backgroundColor = UIColor.init(hex: "EFEFF4")
self.closeButton.color = UIColor.init(hex: "979797")
self.closeButton.backgroundColor = SPNativeColors.lightGray.withAlphaComponent(0.6)
self.closeButton.color = UIColor.init(hex: "979797")
self.addSubview(self.closeButton)
}
@@ -230,36 +237,40 @@ class SPProposeController: SPController {
super.layoutSubviews()
self.titleLabel.sizeToFit()
self.titleLabel.frame.origin.y = self.space * 0.8
self.titleLabel.setWidth(self.frame.width - self.space * 3)
self.titleLabel.setXCenteringFromSuperview()
self.titleLabel.frame.origin.y = self.space * 0.9
self.titleLabel.frame.set(width: self.frame.width - self.space * 3)
self.titleLabel.setXCenter()
self.subtitleLabel.sizeToFit()
self.subtitleLabel.frame.origin.y = self.titleLabel.frame.bottomYPosition + 8
self.subtitleLabel.setWidth(self.frame.width - self.space * 2)
self.subtitleLabel.setXCenteringFromSuperview()
self.subtitleLabel.frame.origin.y = self.titleLabel.frame.bottomY + 8
self.subtitleLabel.frame.set(width: self.frame.width - self.space * 2)
self.subtitleLabel.setXCenter()
self.imageView.frame = CGRect.init(
x: 0, y: self.subtitleLabel.frame.bottomYPosition + self.space / 2,
x: 0, y: self.subtitleLabel.frame.bottomY + self.space / 2,
width: self.imageSideSize,
height: self.imageSideSize
)
self.imageView.setXCenteringFromSuperview()
self.imageView.setXCenter()
self.button.sizeToFit()
self.button.setWidth(self.frame.width - self.space * 2)
self.button.frame.origin.y = self.imageView.frame.bottomYPosition + self.space / 2
self.button.setXCenteringFromSuperview()
self.button.frame.set(height: 52)
self.button.frame.set(width: self.frame.width - self.space * 2)
self.button.frame.origin.y = self.imageView.frame.bottomY + self.space / 1.8
self.button.setXCenter()
self.closeButton.frame = CGRect.init(x: 0, y: 0, width: 24, height: 24)
self.closeButton.frame.origin.x = self.frame.width - self.closeButton.frame.width - 20
self.closeButton.frame.origin.y = 20
self.closeButton.round()
self.frame.set(height: self.button.frame.bottomY + self.space)
}
override func sizeToFit() {
super.sizeToFit()
self.setHeight(self.button.frame.bottomYPosition + self.space)
func layout(origin: CGPoint, width: CGFloat) {
self.frame.origin = origin
self.frame.set(width: width)
self.layoutSubviews()
}
}
@@ -21,17 +21,17 @@
import UIKit
public class SPStatusBarManagerController: UIViewController {
class SPStatusBarManagerController: UIViewController {
var statusBar: SPStatusBar = .dark {
didSet {
UIView.animate(withDuration: 0.3) { () -> Void in
UIView.animate(withDuration: self.statusBarAnimationDuration) { () -> Void in
self.setNeedsStatusBarAppearanceUpdate()
}
}
}
override public var preferredStatusBarStyle: UIStatusBarStyle {
override var preferredStatusBarStyle: UIStatusBarStyle {
switch self.statusBar {
case .dark:
return .default
@@ -40,24 +40,26 @@ public class SPStatusBarManagerController: UIViewController {
}
}
override public var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .slide
}
public var isHiddenStatusBar: Bool = false {
var isHiddenStatusBar: Bool = false {
didSet {
UIView.animate(withDuration: 0.3) { () -> Void in
UIView.animate(withDuration: self.statusBarAnimationDuration) { () -> Void in
self.setNeedsStatusBarAppearanceUpdate()
}
}
}
override public var prefersStatusBarHidden: Bool {
override var prefersStatusBarHidden: Bool {
return isHiddenStatusBar
}
var statusBarAnimationDuration: TimeInterval = 0.3
}
public class SPStatusBarManagerTableController: UITableViewController {
class SPStatusBarManagerTableController: UITableViewController {
var statusBar: SPStatusBar = .dark {
didSet {
@@ -67,7 +69,7 @@ public class SPStatusBarManagerTableController: UITableViewController {
}
}
override public var preferredStatusBarStyle: UIStatusBarStyle {
override var preferredStatusBarStyle: UIStatusBarStyle {
switch self.statusBar {
case .dark:
return .default
@@ -76,11 +78,11 @@ public class SPStatusBarManagerTableController: UITableViewController {
}
}
override public var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .slide
}
public var isHiddenStatusBar: Bool = false {
var isHiddenStatusBar: Bool = false {
didSet {
UIView.animate(withDuration: 0.3) { () -> Void in
self.setNeedsStatusBarAppearanceUpdate()
@@ -88,12 +90,12 @@ public class SPStatusBarManagerTableController: UITableViewController {
}
}
override public var prefersStatusBarHidden: Bool {
override var prefersStatusBarHidden: Bool {
return isHiddenStatusBar
}
}
public class SPStatusBarManagerNavigationController: UINavigationController {
class SPStatusBarManagerNavigationController: UINavigationController {
var statusBar: SPStatusBar = .dark {
didSet {
@@ -103,7 +105,7 @@ public class SPStatusBarManagerNavigationController: UINavigationController {
}
}
override public var preferredStatusBarStyle: UIStatusBarStyle {
override var preferredStatusBarStyle: UIStatusBarStyle {
switch self.statusBar {
case .dark:
return .default
@@ -112,11 +114,11 @@ public class SPStatusBarManagerNavigationController: UINavigationController {
}
}
override public var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .slide
}
public var isHiddenStatusBar: Bool = false {
var isHiddenStatusBar: Bool = false {
didSet {
UIView.animate(withDuration: 0.3) { () -> Void in
self.setNeedsStatusBarAppearanceUpdate()
@@ -124,14 +126,7 @@ public class SPStatusBarManagerNavigationController: UINavigationController {
}
}
override public var prefersStatusBarHidden: Bool {
override var prefersStatusBarHidden: Bool {
return isHiddenStatusBar
}
public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UIView.animate(withDuration: 0.3) { () -> Void in
self.setNeedsStatusBarAppearanceUpdate()
}
}
}
@@ -31,7 +31,10 @@ class SPTableController: SPStatusBarManagerTableController {
self.activityIndicatorView.stopAnimating()
self.activityIndicatorView.color = SPNativeColors.gray
self.view.addSubview(self.activityIndicatorView)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.updateLayout(with: self.view.frame.size)
}
@@ -42,12 +45,6 @@ class SPTableController: SPStatusBarManagerTableController {
}, completion: nil)
}
@available(iOS 11.0, *)
override func viewLayoutMarginsDidChange() {
super.viewLayoutMarginsDidChange()
self.updateLayout(with: self.view.frame.size)
}
func updateLayout(with size: CGSize) {
self.activityIndicatorView.center = CGPoint.init(
x: size.width / 2,
@@ -0,0 +1,67 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
class SPCenterLabelsView: SPView {
let titleLabel = SPLabel()
let subtitleLabel = SPLabel()
override func commonInit() {
super.commonInit()
self.titleLabel.font = UIFont.system(weight: .bold, size: 33)
self.titleLabel.textAlignment = .center
self.titleLabel.textColor = UIColor.black
self.titleLabel.numberOfLines = 0
self.addSubview(self.titleLabel)
self.subtitleLabel.font = UIFont.system(weight: .regular, size: 17)
self.subtitleLabel.textAlignment = .center
self.subtitleLabel.textColor = UIColor.black
self.subtitleLabel.numberOfLines = 0
self.addSubview(self.subtitleLabel)
}
func layout(x: CGFloat, y: CGFloat, width: CGFloat) {
self.frame.origin = CGPoint.init(x: x, y: y)
self.frame.set(width: width)
self.layoutSubviews()
}
override func layoutSubviews() {
super.layoutSubviews()
self.titleLabel.frame.set(width: self.frame.width)
self.titleLabel.sizeToFit()
self.titleLabel.frame.set(width: self.frame.width)
self.titleLabel.frame.origin = CGPoint.zero
self.subtitleLabel.frame.set(width: self.frame.width)
self.subtitleLabel.sizeToFit()
self.subtitleLabel.frame.set(width: self.frame.width)
self.subtitleLabel.frame.origin.x = 0
self.subtitleLabel.frame.origin.y = self.titleLabel.frame.bottomY + 7
self.frame.set(height: self.subtitleLabel.frame.bottomY)
}
}
@@ -21,7 +21,7 @@
import UIKit
class SPEmptyTitlesView: SPView {
class SPEmptyLabelsView: SPView {
let titleLabel = UILabel()
let subtitleLabel = UILabel()
@@ -39,22 +39,22 @@ class SPEmptyTitlesView: SPView {
override func commonInit() {
super.commonInit()
self.titleLabel.font = UIFont.system(type: .Bold, size: 29)
self.titleLabel.font = UIFont.system(weight: .bold, size: 29)
self.titleLabel.textColor = SPNativeColors.gray
self.titleLabel.setCenteringAlignment()
self.titleLabel.setCenterAlignment()
self.titleLabel.numberOfLines = 0
self.addSubview(self.titleLabel)
self.subtitleLabel.font = UIFont.system(type: .Regular, size: 17)
self.subtitleLabel.font = UIFont.system(weight: .regular, size: 17)
self.subtitleLabel.textColor = SPNativeColors.gray
self.subtitleLabel.setCenteringAlignment()
self.subtitleLabel.setCenterAlignment()
self.subtitleLabel.numberOfLines = 0
self.addSubview(self.subtitleLabel)
}
func layout(centerY: CGFloat) {
if let superview = self.superview {
self.setWidth(superview.frame.width * 0.7)
self.frame.set(width: superview.frame.width * 0.7)
self.layoutSubviews()
self.center = CGPoint.init(x: superview.frame.width / 2, y: centerY)
}
@@ -65,12 +65,12 @@ class SPEmptyTitlesView: SPView {
self.titleLabel.frame.origin = .zero
self.titleLabel.sizeToFit()
self.titleLabel.setWidth(self.frame.width)
self.titleLabel.frame.set(width: self.frame.width)
self.subtitleLabel.frame.origin = CGPoint.init(x: 0, y: self.titleLabel.frame.bottomYPosition + 5)
self.subtitleLabel.frame.origin = CGPoint.init(x: 0, y: self.titleLabel.frame.bottomY + 5)
self.subtitleLabel.sizeToFit()
self.subtitleLabel.setWidth(self.frame.width)
self.subtitleLabel.frame.set(width: self.frame.width)
self.setHeight(self.subtitleLabel.frame.bottomYPosition)
self.frame.set(height: self.subtitleLabel.frame.bottomY)
}
}
@@ -21,7 +21,7 @@
import UIKit
public class SPLabel: UILabel {
class SPLabel: UILabel {
init() {
super.init(frame: CGRect.zero)
@@ -23,52 +23,58 @@ import UIKit
class SPSectionLabelsView: SPView {
let titleLabel = UILabel()
let subtitleLabel = UILabel()
let button = UIButton.init(type: UIButton.ButtonType.system)
let titleLabel = SPLabel()
let subtitleLabel = SPLabel()
let button = SPButton()
var titlesInset: CGFloat = 3
override func commonInit() {
super.commonInit()
self.titleLabel.font = UIFont.system(type: .Bold, size: 23)
self.titleLabel.font = UIFont.system(weight: .bold, size: 23)
self.titleLabel.textAlignment = .left
self.titleLabel.textColor = UIColor.black
self.titleLabel.numberOfLines = 0
self.addSubview(self.titleLabel)
self.subtitleLabel.font = UIFont.system(type: .Regular, size: 17)
self.subtitleLabel.font = UIFont.system(weight: .regular, size: 17)
self.subtitleLabel.textAlignment = .left
self.subtitleLabel.textColor = UIColor.black.withAlphaComponent(0.7)
self.subtitleLabel.numberOfLines = 0
self.addSubview(self.subtitleLabel)
self.button.titleLabel?.font = UIFont.system(type: .Regular, size: 17)
self.button.titleLabel?.font = UIFont.system(weight: .regular, size: 17)
self.button.setTitleColor(SPNativeColors.blue, for: UIControl.State.normal)
self.addSubview(self.button)
}
func layout(origin: CGPoint, width: CGFloat) {
self.frame.origin = origin
self.setWidth(width)
self.frame.set(width: width)
self.layoutSubviews()
}
func layout(x: CGFloat, y: CGFloat, width: CGFloat) {
self.layout(origin: CGPoint.init(x: x, y: y), width: width)
}
override func layoutSubviews() {
super.layoutSubviews()
self.titleLabel.frame.set(width: self.frame.width)
self.titleLabel.sizeToFit()
self.titleLabel.setWidth(self.frame.width)
self.titleLabel.frame.origin = CGPoint.zero
self.subtitleLabel.frame.set(width: self.frame.width)
self.subtitleLabel.sizeToFit()
self.subtitleLabel.setWidth(self.frame.width)
self.subtitleLabel.frame.origin.x = 0
self.subtitleLabel.frame.origin.y = self.titleLabel.frame.bottomYPosition + 3
self.subtitleLabel.frame.origin.y = self.titleLabel.frame.bottomY + self.titlesInset
self.button.sizeToFit()
self.button.frame.bottomXPosition = self.frame.width
self.button.frame.bottomYPosition = self.titleLabel.frame.bottomYPosition
self.button.frame.bottomX = self.frame.width
self.button.center.y = self.titleLabel.center.y
self.setHeight(self.subtitleLabel.frame.bottomYPosition)
self.frame.set(height: (self.subtitleLabel.frame.bottomY < 0) ? 0 : self.subtitleLabel.frame.bottomY)
}
}
@@ -21,19 +21,18 @@
import UIKit
@available(iOS 9, *)
public class SPBlurView: UIVisualEffectView {
class SPBlurView: UIVisualEffectView {
private let blurEffect: UIBlurEffect
open var blurRadius: CGFloat {
var blurRadius: CGFloat {
return blurEffect.value(forKeyPath: "blurRadius") as! CGFloat
}
public convenience init() {
convenience init() {
self.init(withRadius: 0)
}
public init(withRadius radius: CGFloat) {
init(withRadius radius: CGFloat) {
let customBlurClass: AnyObject.Type = NSClassFromString("_UICustomBlurEffect")!
let customBlurObject: NSObject.Type = customBlurClass as! NSObject.Type
self.blurEffect = customBlurObject.init() as! UIBlurEffect
@@ -42,11 +41,11 @@ public class SPBlurView: UIVisualEffectView {
super.init(effect: radius == 0 ? nil : self.blurEffect)
}
required public init?(coder aDecoder: NSCoder) {
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
open func setBlurRadius(_ radius: CGFloat) {
func setBlurRadius(_ radius: CGFloat) {
guard radius != blurRadius else {
return
}
@@ -23,7 +23,6 @@ import UIKit
class SPDownloadingImageView: SPImageView {
let activityIndiactorView = UIActivityIndicatorView.init()
let gradeView = UIView.init()
override func commonInit() {
@@ -32,9 +31,6 @@ class SPDownloadingImageView: SPImageView {
self.layer.masksToBounds = true
self.addSubview(self.gradeView)
self.gradeView.backgroundColor = UIColor.init(hex: "F0F1F6")
self.activityIndiactorView.color = UIColor.darkGray
self.addSubview(self.activityIndiactorView)
self.activityIndiactorView.startAnimating()
}
func setImage(link: String, with complection: ((UIImage?)->())? = nil) {
@@ -52,25 +48,21 @@ class SPDownloadingImageView: SPImageView {
self.image = image
if animatable {
self.activityIndiactorView.stopAnimating()
SPAnimation.animate(0.2, animations: {
self.gradeView.alpha = 0
})
} else {
self.activityIndiactorView.stopAnimating()
self.gradeView.alpha = 0
}
}
func startLoading() {
func removeImage() {
self.image = nil
self.activityIndiactorView.startAnimating()
self.gradeView.alpha = 1
}
override func layoutSubviews() {
super.layoutSubviews()
self.gradeView.setEqualsBoundsFromSuperview()
self.activityIndiactorView.center = CGPoint.init(x: self.bounds.midX, y: self.bounds.midY)
self.gradeView.setSuperviewBounds()
}
}
@@ -36,8 +36,7 @@ class SPFooterActionsView: SPView {
func addButton(title: String, titleColor: UIColor, target: @escaping ()->()) {
let button = SPFooterActionButton()
button.setTitle(title)
button.setTitleColor(titleColor)
button.setTitle(title, color: titleColor)
button.target { target() }
self.buttons.append(button)
self.addSubview(button)
@@ -47,19 +46,30 @@ class SPFooterActionsView: SPView {
self.addSubview(separator)
}
func button(for id: Int) -> SPFooterActionButton? {
if (self.buttons.count - 1) < id {
return nil
}
return self.buttons[id]
}
func layout(origin: CGPoint, width: CGFloat) {
self.frame.origin = origin
self.setWidth(width)
self.frame.set(width: width)
self.layoutSubviews()
}
func layout(y: CGFloat, width: CGFloat) {
self.layout(origin: CGPoint.init(x: 19, y: y), width: width)
}
override func layoutSubviews() {
super.layoutSubviews()
self.sectionLabels.layout(origin: CGPoint.zero, width: self.frame.width)
let buttonHeight: CGFloat = 50
var yPositionButton: CGFloat = self.sectionLabels.frame.bottomYPosition + 12
var yPositionButton: CGFloat = self.sectionLabels.frame.bottomY + 12
if !self.buttons.isEmpty {
for i in 0...(buttons.count - 1) {
@@ -68,24 +78,44 @@ class SPFooterActionsView: SPView {
separator.frame.origin.x = 0
separator.frame.origin.y = yPositionButton
separator.setWidth(self.frame.width)
separator.frame.set(width: self.frame.width)
button.frame = CGRect.init(x: 0, y: yPositionButton, width: self.frame.width, height: buttonHeight)
yPositionButton += buttonHeight
}
self.setHeight(yPositionButton)
self.frame.set(height: yPositionButton)
}
}
}
class SPFooterActionButton: SPButton {
var rightIconView: UIView? {
willSet {
self.rightIconView?.removeFromSuperview()
}
didSet {
if let view = self.rightIconView {
self.addSubview(view)
self.layoutSubviews()
}
}
}
override func commonInit() {
super.commonInit()
self.setTitleColor(SPNativeColors.blue)
self.titleLabel?.font = UIFont.system(type: .Regular, size: 21)
self.titleLabel?.font = UIFont.system(weight: .regular, size: 21)
self.contentHorizontalAlignment = .left
}
override func layoutSubviews() {
super.layoutSubviews()
let sideSize: CGFloat = self.frame.height * 0.36
self.rightIconView?.frame = CGRect.init(x: 0, y: 0, width: sideSize, height: sideSize)
self.rightIconView?.center.y = self.frame.height / 2
self.rightIconView?.frame.bottomX = self.frame.width - sideSize / 3
}
}
@@ -21,14 +21,14 @@
import UIKit
public class SPGradientView: SPView {
class SPGradientView: SPView {
var gradientLayer: CAGradientLayer = CAGradientLayer()
var startColor: UIColor = UIColor.white { didSet { self.updateGradient() }}
var endColor: UIColor = UIColor.black { didSet { self.updateGradient() }}
var startColorPosition: Position = Position.TopLeft { didSet { self.updateGradient() }}
var endColorPosition: Position = Position.BottomRight { didSet { self.updateGradient() }}
var startColor = UIColor.white { didSet { self.updateGradient() }}
var endColor = UIColor.black { didSet { self.updateGradient() }}
var startColorPosition = Position.topLeft { didSet { self.updateGradient() }}
var endColorPosition = Position.bottomRight { didSet { self.updateGradient() }}
override func commonInit() {
super.commonInit()
@@ -42,38 +42,39 @@ public class SPGradientView: SPView {
self.gradientLayer.endPoint = self.endColorPosition.point
}
override public func layoutSublayers(of layer: CALayer) {
override func layoutSublayers(of layer: CALayer) {
self.gradientLayer.frame = self.bounds
super.layoutSublayers(of: layer)
}
public enum Position {
case TopLeft
case TopCenter
case TopRight
case BottomLeft
case BottomCenter
case BottomRight
case MediumLeft
case MediumRight
enum Position {
case topLeft
case topCenter
case topRight
case bottomLeft
case bottomCenter
case bottomRight
case mediumLeft
case mediumRight
var point: CGPoint {
switch self {
case .TopLeft:
case .topLeft:
return CGPoint.init(x: 0, y: 0)
case .TopCenter:
case .topCenter:
return CGPoint.init(x: 0.5, y: 0)
case .TopRight:
case .topRight:
return CGPoint.init(x: 1, y: 0)
case .BottomLeft:
case .bottomLeft:
return CGPoint.init(x: 0, y: 1)
case .BottomCenter:
case .bottomCenter:
return CGPoint.init(x: 0.5, y: 1)
case .BottomRight:
case .bottomRight:
return CGPoint.init(x: 1, y: 1)
case .MediumLeft:
case .mediumLeft:
return CGPoint.init(x: 0, y: 0.5)
case .MediumRight:
case .mediumRight:
return CGPoint.init(x: 1, y: 0.5)
}
}

Some files were not shown because too many files have changed in this diff Show More