Compare commits

..

36 Commits

Author SHA1 Message Date
Ivan Vorobei 0c98c3c699 Update to 1.1.6 2018-12-07 13:40:49 +03:00
Ivan Vorobei 4ecc8e96d8 Update Readme.md 2018-12-07 13:40:13 +03:00
Ivan Vorobei fac454f41d remove source 2018-12-07 13:14:31 +03:00
Ivan Vorobei ae68a1be5d Update README.md 2018-12-06 17:33:44 +03:00
Ivan Vorobei c111a20c86 Update README.md 2018-12-06 17:28:05 +03:00
Ivan Vorobei cc1b1ac604 Update SPStorkPresentationController.swift 2018-12-06 17:14:37 +03:00
Ivan Vorobei 265f77dd5e Update README.md 2018-12-06 15:35:02 +03:00
Ivan Vorobei aacf153d0e Update README.md 2018-12-06 15:26:51 +03:00
Ivan Vorobei 5b8fdf6590 Update to 1.1.1 2018-12-06 15:21:50 +03:00
Ivan Vorobei 883ade3052 Update README.md 2018-12-06 15:08:41 +03:00
Ivan Vorobei 44837098ea Update to version 1.1 2018-12-06 15:05:47 +03:00
Ivan Vorobei 42063749ed Update README.md 2018-12-04 12:20:52 +03:00
Ivan Vorobei 464df2eb5c Update README.md 2018-12-04 12:19:31 +03:00
Ivan Vorobei 6f48df5761 Update README.md 2018-12-04 12:10:54 +03:00
Ivan Vorobei e331793afc Update README.md 2018-12-04 12:10:23 +03:00
Ivan Vorobei 82152b698a Update README.md 2018-12-03 14:33:11 +03:00
Ivan Vorobei 75eaaec598 Update Readme 2018-12-03 10:53:25 +03:00
Ivan Vorobei d2cc2e424c Update README.md 2018-12-02 21:13:33 +03:00
Ivan Vorobei 0ac35b4494 Update README.md 2018-12-02 21:13:18 +03:00
Ivan Vorobei 6c2600dea8 Update README.md 2018-12-01 23:36:18 +03:00
Ivan Vorobei 26d7422216 Update README.md 2018-12-01 23:35:01 +03:00
Ivan Vorobei a4a02b598f Update README.md 2018-11-30 20:54:40 +03:00
Ivan Vorobei 0309bb2e0e Update example and Readme 2018-11-30 20:43:35 +03:00
Ivan Vorobei 4e0507f132 Update README.md 2018-11-30 20:33:04 +03:00
Ivan Vorobei 8e33ff7614 Add donate banner 2018-11-30 20:32:36 +03:00
Ivan Vorobei b7ab9e0327 Update README.md 2018-11-30 20:30:39 +03:00
Ivan Vorobei 4129f23d00 Add header banner 2018-11-30 20:29:57 +03:00
Ivan Vorobei 91b01d83d9 Update Readme 2018-11-30 20:26:33 +03:00
Ivan Vorobei 4eda691bd0 Update README.md 2018-11-30 20:25:06 +03:00
Ivan Vorobei 0889352299 Update Readme 2018-11-30 20:18:38 +03:00
Ivan Vorobei 34f267d43c Update SPStorkController.podspec 2018-11-29 12:55:29 +03:00
Ivan Vorobei 425f8ec1ca Fix layout for SPStorkController
- Fix layout for SPStorkController
- Add extenshion for `CGRect` static var `displayFrame`. It is rect for screen size
- Set clear background color for `SPStorkIndicatorView`
2018-11-29 12:49:56 +03:00
Ivan Vorobei 25ea7eab4c Add more interactive to SPStorkController 2018-11-29 11:10:18 +03:00
Ivan Vorobei 16f060820f Update README.md 2018-11-28 20:11:41 +03:00
Ivan Vorobei 4c8faf7695 Update README.md 2018-11-28 17:49:37 +03:00
Ivan Vorobei 5b843582d4 Update README.md 2018-11-28 17:30:29 +03:00
156 changed files with 2826 additions and 3839 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,62 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
public struct SPStorkController {
static public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if let controller = self.controller(for: scrollView) {
if let presentationController = controller.presentationController as? SPStorkPresentationController {
let translation = -(scrollView.contentOffset.y + scrollView.contentInset.top)
if translation >= 0 {
scrollView.subviews.forEach {
$0.transform = CGAffineTransform(translationX: 0, y: -translation)
}
if presentationController.pan?.state != UIGestureRecognizer.State.changed {
presentationController.scrollViewDidScroll(translation)
}
} else {
presentationController.scrollViewDidScroll(0)
}
}
}
}
static private func controller(for view: UIView) -> UIViewController? {
var nextResponder = view.next
while nextResponder != nil && !(nextResponder! is UIViewController) {
nextResponder = nextResponder!.next
}
return nextResponder as? UIViewController
}
private init() {}
}
extension UIViewController {
var isPresentedAsStork: Bool {
return transitioningDelegate is SPStorkTransitioningDelegate
&& modalPresentationStyle == .custom
&& presentingViewController != nil
}
}
@@ -28,7 +28,7 @@ final class SPStorkDismissingAnimationController: NSObject, UIViewControllerAnim
guard let presentedViewController = transitionContext.viewController(forKey: .from) else {
return
}
let containerView = transitionContext.containerView
let offscreenFrame = CGRect(x: 0, y: containerView.bounds.height, width: containerView.bounds.width, height: containerView.bounds.height)
@@ -41,7 +41,7 @@ final class SPStorkDismissingAnimationController: NSObject, UIViewControllerAnim
animations: {
presentedViewController.view.frame = offscreenFrame
}) { finished in
transitionContext.completeTransition(finished)
transitionContext.completeTransition(finished)
}
}
@@ -27,16 +27,16 @@ class SPStorkIndicatorView: UIView {
didSet {
switch self.style {
case .line:
SPAnimationSpring.animate(0.5, animations: {
self.animate {
self.leftView.transform = .identity
self.rightView.transform = .identity
}, options: .curveEaseOut)
}
case .arrow:
SPAnimationSpring.animate(0.5, animations: {
self.animate {
let angle = CGFloat(20 * Float.pi / 180)
self.leftView.transform = CGAffineTransform.init(rotationAngle: angle)
self.rightView.transform = CGAffineTransform.init(rotationAngle: -angle)
}, options: .curveEaseOut)
}
}
}
@@ -47,32 +47,37 @@ class SPStorkIndicatorView: UIView {
init() {
super.init(frame: .zero)
self.backgroundColor = UIColor.clear
self.addSubview(self.leftView)
self.addSubview(self.rightView)
self.leftView.backgroundColor = UIColor.init(hex: "CAC9CF")
self.rightView.backgroundColor = UIColor.init(hex: "CAC9CF")
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)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func sizeToFit() {
super.sizeToFit()
self.setWidth(36)
self.setHeight(13)
self.frame = CGRect.init(x: self.frame.origin.x, y: self.frame.origin.y, width: 36, height: 13)
let height: CGFloat = 5
let correction = height / 2
self.leftView.frame = CGRect.init(x: 0, y: 0, width: self.frame.width / 2 + correction, height: height)
self.leftView.center.y = self.frame.height / 2
self.leftView.round()
self.leftView.layer.cornerRadius = min(self.leftView.frame.width, self.leftView.frame.height) / 2
self.rightView.frame = CGRect.init(x: self.frame.width / 2 - correction, y: 0, width: self.frame.width / 2 + correction, height: height)
self.rightView.center.y = self.frame.height / 2
self.rightView.round()
self.rightView.layer.cornerRadius = min(self.leftView.frame.width, self.leftView.frame.height) / 2
}
private func animate(animations: @escaping (() -> Void)) {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.beginFromCurrentState, .curveEaseOut], animations: {
animations()
})
}
enum Style {
@@ -0,0 +1,396 @@
// 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 SPStorkPresentationController: UIPresentationController, UIGestureRecognizerDelegate {
var isSwipeToDismissEnabled: Bool = true
var showIndicator: Bool = true
var transitioningDelegate: SPStorkTransitioningDelegate?
var pan: UIPanGestureRecognizer?
private var indicatorView = SPStorkIndicatorView()
private var gradeView: UIView = UIView()
private let snapshotViewContainer = UIView()
private var snapshotView: UIView?
private let backgroundView = UIView()
private var snapshotViewTopConstraint: NSLayoutConstraint?
private var snapshotViewWidthConstraint: NSLayoutConstraint?
private var snapshotViewAspectRatioConstraint: NSLayoutConstraint?
private var workGester: Bool = false
private var startDismissing: 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 var scaleForPresentingView: CGFloat {
guard let containerView = containerView else { return 0 }
let factor = 1 - (topSpace * 2 / containerView.frame.height)
return factor
}
override var frameOfPresentedViewInContainerView: CGRect {
guard let containerView = containerView else { return .zero }
let yOffset: CGFloat = topSpace + 13
return CGRect(x: 0, y: yOffset, width: containerView.bounds.width, height: containerView.bounds.height - yOffset)
}
override func presentationTransitionWillBegin() {
super.presentationTransitionWillBegin()
guard let containerView = self.containerView, let presentedView = self.presentedView, let window = containerView.window else { return }
if self.showIndicator {
presentedView.addSubview(self.indicatorView)
}
self.updateLayoutIndicator()
self.indicatorView.style = .arrow
self.gradeView.alpha = 0
let initialFrame: CGRect = presentingViewController.isPresentedAsStork ? presentingViewController.view.frame : containerView.bounds
containerView.insertSubview(self.snapshotViewContainer, belowSubview: presentedViewController.view)
self.snapshotViewContainer.frame = initialFrame
self.updateSnapshot()
self.backgroundView.backgroundColor = UIColor.black
self.backgroundView.translatesAutoresizingMaskIntoConstraints = false
containerView.insertSubview(self.backgroundView, belowSubview: self.snapshotViewContainer)
NSLayoutConstraint.activate([
self.backgroundView.topAnchor.constraint(equalTo: window.topAnchor),
self.backgroundView.leftAnchor.constraint(equalTo: window.leftAnchor),
self.backgroundView.rightAnchor.constraint(equalTo: window.rightAnchor),
self.backgroundView.bottomAnchor.constraint(equalTo: window.bottomAnchor)
])
let transformForSnapshotView = CGAffineTransform.identity
.translatedBy(x: 0, y: -snapshotViewContainer.frame.origin.y)
.translatedBy(x: 0, y: self.topSpace)
.translatedBy(x: 0, y: -snapshotViewContainer.frame.height / 2)
.scaledBy(x: scaleForPresentingView, y: scaleForPresentingView)
.translatedBy(x: 0, y: snapshotViewContainer.frame.height / 2)
self.addCornerRadiusAnimation(for: self.snapshotView, cornerRadius: self.cornerRadius, duration: 0.6)
self.snapshotView?.layer.masksToBounds = true
if #available(iOS 11.0, *) {
presentedView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
}
presentedView.layer.cornerRadius = self.cornerRadius
presentedView.layer.masksToBounds = true
var rootSnapshotView: UIView?
var rootSnapshotRoundedView: UIView?
if presentingViewController.isPresentedAsStork {
guard let rootController = presentingViewController.presentingViewController, let snapshotView = rootController.view.snapshotView(afterScreenUpdates: false) else { return }
containerView.insertSubview(snapshotView, aboveSubview: self.backgroundView)
snapshotView.frame = initialFrame
snapshotView.transform = transformForSnapshotView
snapshotView.alpha = self.alpha
snapshotView.layer.cornerRadius = self.cornerRadius
snapshotView.layer.masksToBounds = true
rootSnapshotView = snapshotView
let snapshotRoundedView = UIView()
snapshotRoundedView.layer.cornerRadius = self.cornerRadius
snapshotRoundedView.layer.masksToBounds = true
containerView.insertSubview(snapshotRoundedView, aboveSubview: snapshotView)
snapshotRoundedView.frame = initialFrame
snapshotRoundedView.transform = transformForSnapshotView
rootSnapshotRoundedView = snapshotRoundedView
}
presentedViewController.transitionCoordinator?.animate(
alongsideTransition: { [weak self] context in
guard let `self` = self else { return }
self.snapshotView?.transform = transformForSnapshotView
self.gradeView.alpha = self.alpha
}, completion: { _ in
self.snapshotView?.transform = .identity
rootSnapshotView?.removeFromSuperview()
rootSnapshotRoundedView?.removeFromSuperview()
})
}
override func presentationTransitionDidEnd(_ completed: Bool) {
super.presentationTransitionDidEnd(completed)
guard let containerView = containerView else { return }
self.presentedViewController.view.frame = self.frameOfPresentedViewInContainerView
self.snapshotViewContainer.transform = .identity
self.snapshotViewContainer.translatesAutoresizingMaskIntoConstraints = false
self.snapshotViewContainer.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
self.updateSnapshotAspectRatio()
if self.isSwipeToDismissEnabled {
self.pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
self.pan!.delegate = self
self.pan!.maximumNumberOfTouches = 1
self.pan!.cancelsTouchesInView = false
self.presentedViewController.view.addGestureRecognizer(self.pan!)
}
}
override func dismissalTransitionWillBegin() {
super.dismissalTransitionWillBegin()
guard let containerView = containerView else { return }
self.startDismissing = true
let initialFrame: CGRect = presentingViewController.isPresentedAsStork ? presentingViewController.view.frame : containerView.bounds
let initialTransform = CGAffineTransform.identity
.translatedBy(x: 0, y: -initialFrame.origin.y)
.translatedBy(x: 0, y: self.topSpace)
.translatedBy(x: 0, y: -initialFrame.height / 2)
.scaledBy(x: scaleForPresentingView, y: scaleForPresentingView)
.translatedBy(x: 0, y: initialFrame.height / 2)
self.snapshotViewTopConstraint?.isActive = false
self.snapshotViewWidthConstraint?.isActive = false
self.snapshotViewAspectRatioConstraint?.isActive = false
self.snapshotViewContainer.translatesAutoresizingMaskIntoConstraints = true
self.snapshotViewContainer.frame = initialFrame
self.snapshotViewContainer.transform = initialTransform
let finalCornerRadius = presentingViewController.isPresentedAsStork ? self.cornerRadius : 0
let finalTransform: CGAffineTransform = .identity
self.addCornerRadiusAnimation(for: self.snapshotView, cornerRadius: finalCornerRadius, duration: 0.6)
var rootSnapshotView: UIView?
var rootSnapshotRoundedView: UIView?
if presentingViewController.isPresentedAsStork {
guard let rootController = presentingViewController.presentingViewController, let snapshotView = rootController.view.snapshotView(afterScreenUpdates: false) else { return }
containerView.insertSubview(snapshotView, aboveSubview: backgroundView)
snapshotView.frame = initialFrame
snapshotView.transform = initialTransform
rootSnapshotView = snapshotView
snapshotView.layer.cornerRadius = self.cornerRadius
snapshotView.layer.masksToBounds = true
let snapshotRoundedView = UIView()
snapshotRoundedView.layer.cornerRadius = self.cornerRadius
snapshotRoundedView.layer.masksToBounds = true
snapshotRoundedView.backgroundColor = UIColor.black.withAlphaComponent(1 - self.alpha)
containerView.insertSubview(snapshotRoundedView, aboveSubview: snapshotView)
snapshotRoundedView.frame = initialFrame
snapshotRoundedView.transform = initialTransform
rootSnapshotRoundedView = snapshotRoundedView
}
presentedViewController.transitionCoordinator?.animate(
alongsideTransition: { [weak self] context in
guard let `self` = self else { return }
self.snapshotView?.transform = .identity
self.snapshotViewContainer.transform = finalTransform
self.gradeView.alpha = 0
}, completion: { _ in
rootSnapshotView?.removeFromSuperview()
rootSnapshotRoundedView?.removeFromSuperview()
})
}
override func dismissalTransitionDidEnd(_ completed: Bool) {
super.dismissalTransitionDidEnd(completed)
guard let containerView = containerView else { return }
self.backgroundView.removeFromSuperview()
self.snapshotView?.removeFromSuperview()
self.snapshotViewContainer.removeFromSuperview()
let offscreenFrame = CGRect(x: 0, y: containerView.bounds.height, width: containerView.bounds.width, height: containerView.bounds.height)
presentedViewController.view.frame = offscreenFrame
presentedViewController.view.transform = .identity
}
}
extension SPStorkPresentationController {
@objc func handlePan(gestureRecognizer: UIPanGestureRecognizer) {
guard gestureRecognizer.isEqual(pan), self.isSwipeToDismissEnabled else { return }
switch gestureRecognizer.state {
case .began:
self.workGester = true
self.indicatorView.style = .line
self.presentingViewController.view.layer.removeAllAnimations()
gestureRecognizer.setTranslation(CGPoint(x: 0, y: 0), in: containerView)
case .changed:
self.workGester = true
if self.isSwipeToDismissEnabled {
let translation = gestureRecognizer.translation(in: presentedView)
self.updatePresentedViewForTranslation(inVerticalDirection: translation.y)
} else {
gestureRecognizer.setTranslation(.zero, in: presentedView)
}
case .ended:
self.workGester = false
let translation = gestureRecognizer.translation(in: presentedView).y
if translation >= 240 {
presentedViewController.dismiss(animated: true, completion: nil)
} else {
self.indicatorView.style = .arrow
UIView.animate(
withDuration: 0.6,
delay: 0,
usingSpringWithDamping: 1,
initialSpringVelocity: 1,
options: [.curveEaseOut, .allowUserInteraction],
animations: {
self.snapshotView?.transform = .identity
self.presentedView?.transform = .identity
self.gradeView.alpha = self.alpha
})
}
default:
break
}
}
func scrollViewDidScroll(_ translation: CGFloat) {
if !self.workGester {
self.updatePresentedViewForTranslation(inVerticalDirection: translation)
}
}
private func updatePresentedViewForTranslation(inVerticalDirection translation: CGFloat) {
if self.startDismissing { return }
let elasticThreshold: CGFloat = 120
let translationFactor: CGFloat = 1 / 2
if translation >= 0 {
let translationForModal: CGFloat = {
if translation >= elasticThreshold {
let frictionLength = translation - elasticThreshold
let frictionTranslation = 30 * atan(frictionLength / 120) + frictionLength / 10
return frictionTranslation + (elasticThreshold * translationFactor)
} else {
return translation * translationFactor
}
}()
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)
}
}
}
extension SPStorkPresentationController {
override func containerViewWillLayoutSubviews() {
super.containerViewWillLayoutSubviews()
guard let containerView = containerView else { return }
self.updateSnapshotAspectRatio()
if presentedViewController.view.isDescendant(of: containerView) {
UIView.animate(withDuration: 0.1) { [weak self] in
guard let `self` = self else { return }
self.presentedViewController.view.frame = self.frameOfPresentedViewInContainerView
}
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { contex in
self.updateLayoutIndicator()
}, completion: { [weak self] _ in
self?.updateSnapshotAspectRatio()
self?.updateSnapshot()
})
}
private func updateLayoutIndicator() {
guard let presentedView = self.presentedView else { return }
self.indicatorView.style = .line
self.indicatorView.sizeToFit()
self.indicatorView.frame.origin.y = 12
self.indicatorView.center.x = presentedView.frame.width / 2
}
private func updateSnapshot() {
guard let currentSnapshotView = presentingViewController.view.snapshotView(afterScreenUpdates: true) else { return }
self.snapshotView?.removeFromSuperview()
self.snapshotViewContainer.addSubview(currentSnapshotView)
self.constraints(view: currentSnapshotView, to: self.snapshotViewContainer)
self.snapshotView = currentSnapshotView
self.gradeView.removeFromSuperview()
self.gradeView.backgroundColor = UIColor.black
self.snapshotView!.addSubview(self.gradeView)
self.constraints(view: self.gradeView, to: self.snapshotView!)
}
private func updateSnapshotAspectRatio() {
guard let containerView = containerView, snapshotViewContainer.translatesAutoresizingMaskIntoConstraints == false else { return }
self.snapshotViewTopConstraint?.isActive = false
self.snapshotViewWidthConstraint?.isActive = false
self.snapshotViewAspectRatioConstraint?.isActive = false
let snapshotReferenceSize = presentingViewController.view.frame.size
let aspectRatio = snapshotReferenceSize.width / snapshotReferenceSize.height
self.snapshotViewTopConstraint = snapshotViewContainer.topAnchor.constraint(equalTo: containerView.topAnchor, constant: self.topSpace)
self.snapshotViewWidthConstraint = snapshotViewContainer.widthAnchor.constraint(equalTo: containerView.widthAnchor, multiplier: scaleForPresentingView)
self.snapshotViewAspectRatioConstraint = snapshotViewContainer.widthAnchor.constraint(equalTo: snapshotViewContainer.heightAnchor, multiplier: aspectRatio)
self.snapshotViewTopConstraint?.isActive = true
self.snapshotViewWidthConstraint?.isActive = true
self.snapshotViewAspectRatioConstraint?.isActive = true
}
private func constraints(view: UIView, to superView: UIView) {
view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
view.topAnchor.constraint(equalTo: superView.topAnchor),
view.leftAnchor.constraint(equalTo: superView.leftAnchor),
view.rightAnchor.constraint(equalTo: superView.rightAnchor),
view.bottomAnchor.constraint(equalTo: superView.bottomAnchor)
])
}
private func addCornerRadiusAnimation(for view: UIView?, cornerRadius: CGFloat, duration: CFTimeInterval) {
guard let view = view else { return }
let animation = CABasicAnimation(keyPath:"cornerRadius")
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
animation.fromValue = view.layer.cornerRadius
animation.toValue = cornerRadius
animation.duration = duration
view.layer.add(animation, forKey: "cornerRadius")
view.layer.cornerRadius = cornerRadius
}
}
@@ -24,9 +24,11 @@ import UIKit
final class SPStorkPresentingAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
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)
@@ -23,6 +23,9 @@ import UIKit
public final class SPStorkTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
public var isSwipeToDismissEnabled: Bool = true
public var showIndicator: Bool = true
public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
let controller = SPStorkPresentationController(presentedViewController: presented, presenting: presenting)
controller.transitioningDelegate = self
@@ -21,38 +21,40 @@
import UIKit
extension UIBezierPath {
struct SPApp {
func resizeTo(width: CGFloat) {
let currentWidth = self.bounds.width
let relativeFactor = width / currentWidth
self.apply(CGAffineTransform(scaleX: relativeFactor, y: relativeFactor))
static var udid: String? {
return UIDevice.current.identifierForVendor?.uuidString
}
func convertToImage(fill: Bool, stroke: Bool, color: UIColor = .black) -> UIImage {
UIGraphicsBeginImageContextWithOptions(CGSize(width: self.bounds.width, height: self.bounds.height), false, 0.0)
let context = UIGraphicsGetCurrentContext()
context!.setStrokeColor(color.cgColor)
context!.setFillColor(color.cgColor)
if stroke {
self.stroke()
static var displayName: String? {
return Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
}
static var rootController: UIViewController? {
return UIApplication.shared.keyWindow?.rootViewController
}
static func set(rootController: UIViewController, animatable: Bool = true) {
rootController.view.frame = UIScreen.main.bounds
let replaceRootViewController = {
UIApplication.shared.keyWindow?.rootViewController = rootController
}
if fill {
self.fill()
if animatable {
UIView.transition(
with: UIApplication.shared.keyWindow ?? UIWindow(),
duration: 0.5,
options: UIView.AnimationOptions.transitionCrossDissolve,
animations: {
replaceRootViewController()
}, completion: nil)
} else {
replaceRootViewController()
}
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
public struct SPBezierPath {
public static func setContext() {
UIGraphicsBeginImageContextWithOptions(CGSize(width: 1, height: 1), false, 0)
}
public static func endContext() {
UIGraphicsEndImageContext()
}
private init() {}
}
@@ -0,0 +1,60 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
import LocalAuthentication
struct SPLocalAuthentication {
static var isEnable: Bool {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
return true
} else {
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
return true
} else {
return false
}
}
}
static func request(reason: String, complecton: @escaping (Bool)->()) {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, error in
DispatchQueue.main.async { complecton(success) }
}
} else {
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) { success, error in
DispatchQueue.main.async { complecton(success) }
}
} else {
complecton(false)
}
}
}
private init() {}
}
@@ -21,14 +21,18 @@
import UIKit
extension UITableViewController {
public struct SPConstraints {
func refreshManually() {
self.refreshControl?.beginRefreshing()
self.tableView.setContentOffset(
CGPoint.init(
x: 0,
y: self.tableView.contentOffset.y - (self.refreshControl?.frame.size.height ?? 0)
), animated: true)
static func setEqualSize(_ view: UIView, superVuew: UIView) {
view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
view.topAnchor.constraint(equalTo: superVuew.topAnchor),
view.leftAnchor.constraint(equalTo: superVuew.leftAnchor),
view.rightAnchor.constraint(equalTo: superVuew.rightAnchor),
view.bottomAnchor.constraint(equalTo: superVuew.bottomAnchor)
])
}
}
@@ -24,11 +24,11 @@ import UIKit
struct SPDevice {
static var isIphone: Bool {
return UIDevice.current.isIphone
return UIDevice.current.userInterfaceIdiom == .phone
}
static var isIpad: Bool {
return UIDevice.current.isIpad
return UIDevice.current.userInterfaceIdiom == .pad
}
struct Orientation {
@@ -30,6 +30,7 @@ struct SPDownloader {
}
return
}
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
@@ -47,6 +48,8 @@ struct SPDownloader {
}
}.resume()
}
private init() {}
}
@@ -23,10 +23,8 @@ import Foundation
extension Array {
func takeElements(count: Int) -> Array {
if (count < self.count) {
return Array(self[0..<count])
}
func get(count: Int) -> Array {
if (count < self.count) { return Array(self[0..<count]) }
return Array(self)
}
}
@@ -35,11 +33,8 @@ extension Array where Element: Equatable {
mutating func removeDuplicates() {
var result = [Element]()
for value in self {
if result.contains(value) == false {
result.append(value)
}
if result.contains(value) == false { result.append(value) }
}
self = result
}
@@ -48,9 +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.index(of: item), index + 1 < self.count { return self[index + 1] }
return nil
}
}
@@ -24,28 +24,16 @@ import UIKit
extension CGRect {
var bottomXPosition: CGFloat {
get {
return self.origin.x + self.width
}
set {
self.origin.x = newValue - self.width
}
get { return self.origin.x + self.width }
set { self.origin.x = newValue - self.width }
}
var bottomYPosition: CGFloat {
get {
return self.origin.y + self.height
}
set {
self.origin.y = newValue - self.height
}
get { return self.origin.y + self.height }
set { self.origin.y = newValue - self.height }
}
var minSideSize: CGFloat {
return min(self.width, self.height)
}
var isWidthLessThanHeight: Bool {
return self.width < self.height
}
}
@@ -23,15 +23,15 @@ import UIKit
extension CGSize {
func resize(newWidth: CGFloat) -> CGSize {
func resize(width: CGFloat) -> CGSize {
let relativeSideSize = self.width / self.height
let newHeight = newWidth / relativeSideSize
return CGSize.init(width: newWidth, height: newHeight)
let newHeight = width / relativeSideSize
return CGSize.init(width: width, height: newHeight)
}
func resize(newHeight: CGFloat) -> CGSize {
func resize(height: CGFloat) -> CGSize {
let relativeSideSize = self.width / self.height
let newWidth = newHeight * relativeSideSize
return CGSize.init(width: newWidth, height: newHeight)
let newWidth = height * relativeSideSize
return CGSize.init(width: newWidth, height: height)
}
}
@@ -66,20 +66,6 @@ extension String {
return false
}
mutating func reduce(minimumFractionDigits: Int = 0, maximumFractionDigits: Int) {
let formatter = NumberFormatter()
formatter.minimumFractionDigits = minimumFractionDigits
formatter.maximumFractionDigits = maximumFractionDigits
let int = Double(self)
if int != nil {
let number = NSNumber.init(value: int!)
if var newValue = formatter.string(from: number) {
newValue.replace(",", with: ".")
self = newValue
}
}
}
mutating func replace(_ replacingString: String, with newString: String) {
self = self.replacingOccurrences(of: replacingString, with: newString)
}
@@ -65,7 +65,8 @@ extension UIButton {
}
func setTitleColor(_ color: UIColor) {
self.setTitleColorForNoramlAndHightlightedStates(color: color)
self.setTitleColor(color, for: .normal)
self.setTitleColor(color.withAlphaComponent(0.7), for: .highlighted)
}
func removeAllTargets() {
@@ -74,23 +75,18 @@ extension UIButton {
func showText(_ text: String, withComplection completion: (() -> Void)! = {}) {
let baseText = self.titleLabel?.text ?? " "
SPAnimation.animate(0.2,
animations: {
self.titleLabel?.alpha = 0
SPAnimation.animate(0.2, animations: {
self.titleLabel?.alpha = 0
}, withComplection: {
self.setTitle(text, for: .normal)
SPAnimation.animate(0.2, animations: {
self.titleLabel?.alpha = 1
}, withComplection: {
SPAnimation.animate(0.2, animations: {
self.titleLabel?.alpha = 0
}, delay: 0.35,
withComplection: {
self.setTitle(baseText, for: .normal)
SPAnimation.animate(0.2, animations: {
self.titleLabel?.alpha = 1
}, withComplection: {
@@ -102,13 +98,10 @@ extension UIButton {
}
func setAnimatableText(_ text: String, withComplection completion: (() -> Void)! = {}) {
SPAnimation.animate(0.2,
animations: {
self.titleLabel?.alpha = 0
SPAnimation.animate(0.2, animations: {
self.titleLabel?.alpha = 0
}, withComplection: {
self.setTitle(text, for: .normal)
SPAnimation.animate(0.2, animations: {
self.titleLabel?.alpha = 1
}, withComplection: {
@@ -118,25 +111,18 @@ extension UIButton {
}
func hideContent(completion: (() -> Void)! = {}) {
SPAnimation.animate(0.2,
animations: {
self.titleLabel?.alpha = 0
SPAnimation.animate(0.2, animations: {
self.titleLabel?.alpha = 0
}, withComplection: {
completion()
})
}
func showContent(completion: (() -> Void)! = {}) {
SPAnimation.animate(0.2,
animations: {
self.titleLabel?.alpha = 1
SPAnimation.animate(0.2, animations: {
self.titleLabel?.alpha = 1
}, withComplection: {
completion()
})
}
func setTitleColorForNoramlAndHightlightedStates(color: UIColor) {
self.setTitleColor(color, for: .normal)
self.setTitleColor(color.withAlphaComponent(0.7), for: .highlighted)
}
}
@@ -42,7 +42,7 @@ public extension UIFont {
return UIFont.init(
name: self.getFontNameBy(fontType: fontType) + self.getBoldTypeNameBy(boldType: boldType),
size: size
)!
)!
}
private static func getFontNameBy(fontType: FontType) -> String {
@@ -21,13 +21,15 @@
import UIKit
extension UIScreen {
public extension UIImage {
var minSideSize: CGFloat {
return min(UIScreen.main.bounds.width, UIScreen.main.bounds.height)
}
var widthLessThanHeight: Bool {
return UIScreen.main.bounds.width < UIScreen.main.bounds.height
public func resize(width: CGFloat) -> UIImage {
let scale = width / self.size.width
let newHeight = self.size.height * scale
UIGraphicsBeginImageContext(CGSize(width: width, height: newHeight))
self.draw(in: CGRect(x: 0, y: 0, width: width, height: newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
@@ -23,17 +23,12 @@ import UIKit
extension UIImageView {
public func setNativeStyle() {
public func setNative() {
self.layer.borderWidth = 0.5
self.layer.borderColor = SPNativeStyleKit.Colors.midGray.cgColor
self.layer.masksToBounds = true
}
public func removeNativeStyle() {
self.layer.borderWidth = 0
self.layer.borderColor = UIColor.clear.cgColor
}
public func downloadedFrom(url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit, withComplection complection: @escaping (UIImage?) -> () = {_ in }) {
DispatchQueue.main.async {
self.contentMode = mode
@@ -25,7 +25,11 @@ extension UINavigationController {
static var elementsColor: UIColor {
get {
return UINavigationBar.appearance().tintColor
if UINavigationBar.appearance().tintColor != nil {
return UINavigationBar.appearance().tintColor
} else {
return SPNativeStyleKit.Colors.blue
}
}
set {
UINavigationBar.appearance().tintColor = newValue
@@ -0,0 +1,39 @@
// 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 UITabBarController {
func addTabBarItem(title: String, image: UIImage, selectedImage: UIImage? = nil, controller: UIViewController) {
let tabBarItem = UITabBarItem(
title: title,
image: image,
selectedImage: selectedImage ?? image
)
controller.tabBarItem = tabBarItem
if self.viewControllers == nil { self.viewControllers = [controller] }
else { self.viewControllers?.append(controller) }
}
}
@@ -36,50 +36,24 @@ extension UITableView {
}
var lastSectionWithRows: Int? {
if self.numberOfSections == 0 {
return nil
}
if self.numberOfSections == 0 { return nil }
var section = self.numberOfSections - 1
if section < 0 {
return nil
}
if section < 0 { return nil }
while section >= 0 {
if self.numberOfRows(inSection: section) != 0 {
return section
}
if self.numberOfRows(inSection: section) != 0 { return section }
section -= 1
}
return nil
}
var firstSectionWithRows: Int? {
if self.numberOfSections == 0 {
return nil
}
if self.numberOfSections == 0 { return nil }
var section = 0
if section > self.numberOfSections - 1 {
return nil
}
if section > self.numberOfSections - 1 { return nil }
while section <= (self.numberOfSections - 1) {
if self.numberOfRows(inSection: section) != 0 {
return section
}
if self.numberOfRows(inSection: section) != 0 { return section }
section += 1
}
return nil
}
}
@@ -39,7 +39,6 @@ extension UIViewController {
}
}
//MARK: - Keyboard
extension UIViewController {
func dismissKeyboardWhenTappedAround() {
@@ -53,16 +52,13 @@ extension UIViewController {
}
}
//MARK: - Add image to Library
extension UIViewController {
func save(image: UIImage) {
if SPPermission.isAllow(.photoLibrary) {
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized {
UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.image(_:didFinishSavingWithError:contextInfo:)), nil)
} else {
SPPermission.request(.photoLibrary) {
UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.image(_:didFinishSavingWithError:contextInfo:)), nil)
}
print("Saving image error. Not allowed permission")
}
}
@@ -107,7 +103,6 @@ extension UIViewController {
}
}
//MARK: - Navigation Bar
extension UIViewController {
func setPrefersLargeNavigationTitle(_ title: String, smallScreenToSmallBar: Bool = true) {
@@ -129,31 +124,36 @@ extension UIViewController {
switch style {
case .large:
if #available(iOS 11.0, *) {
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationItem.largeTitleDisplayMode = .always
}
case .small:
if #available(iOS 11.0, *) {
self.navigationItem.largeTitleDisplayMode = .never
}
}
case .stork:
if #available(iOS 11.0, *) {
self.navigationItem.largeTitleDisplayMode = .never
}
}
}
}
extension UIViewController {
var topSafeArea: CGFloat {
return self.view.topSafeArea
}
var bottomSafeArea: CGFloat {
return self.view.bottomSafeArea
var safeArea: UIEdgeInsets {
if #available(iOS 11.0, *) {
return self.view.safeAreaInsets
} else {
return UIEdgeInsets.zero
}
}
var navigationBarHeight: CGFloat {
return self.navigationController?.navigationBar.frame.height ?? 0
}
var statusBarHeight: CGFloat {
static var statusBarHeight: CGFloat {
return UIApplication.shared.statusBarFrame.height
}
}
@@ -21,7 +21,17 @@
import UIKit
// MARK: - layout
public extension UIView {
var viewController: UIViewController? {
get {
if let nextResponder = self.next as? UIViewController { return nextResponder }
else if let nextResponder = self.next as? UIView { return nextResponder.viewController }
else { return nil }
}
}
}
public extension UIView {
var topSafeArea: CGFloat {
@@ -49,21 +59,16 @@ public extension UIView {
}
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) {
var width = bounds.width * widthFactor
if maxWidth != nil {
width.setIfMore(when: maxWidth!)
}
if maxWidth != nil { width.setIfMore(when: maxWidth!) }
var height = bounds.height * heightFactor
if maxHeight != nil {
height.setIfMore(when: maxHeight!)
}
if maxHeight != nil { height.setIfMore(when: maxHeight!) }
self.frame = CGRect.init(x: 0, y: 0, width: width, height: height)
@@ -74,27 +79,19 @@ public extension UIView {
}
func setEqualsBoundsFromSuperview(customWidth: CGFloat? = nil, customHeight: CGFloat? = nil) {
if self.superview == nil {
return
}
if self.superview == nil { return }
self.frame = CGRect.init(origin: CGPoint.zero, size: self.superview!.frame.size)
if customWidth != nil {
self.frame = CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: customWidth!, height: self.frame.height))
}
if customHeight != nil {
self.frame = CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: self.frame.width, height: customHeight!))
}
}
func resize(newWidth width: CGFloat) {
func resize(width: CGFloat) {
let relativeFactor = self.frame.width / self.frame.height
if relativeFactor.isNaN {
return
}
if relativeFactor.isNaN { return }
self.frame = CGRect.init(
x: self.frame.origin.x,
y: self.frame.origin.y,
@@ -103,11 +100,9 @@ public extension UIView {
)
}
func resize(newHeight height: CGFloat) {
func resize(height: CGFloat) {
let relativeFactor = self.frame.width / self.frame.height
if relativeFactor.isNaN {
return
}
if relativeFactor.isNaN { return }
self.frame = CGRect.init(
x: self.frame.origin.x,
y: self.frame.origin.y,
@@ -116,6 +111,10 @@ public extension UIView {
)
}
func setYCenteringFromSuperview() {
self.center.y = (self.superview?.frame.height ?? 0) / 2
}
func setXCenteringFromSuperview() {
self.center.x = (self.superview?.frame.width ?? 0) / 2
}
@@ -148,29 +147,19 @@ public extension UIView {
}
}
// MARK: - convertToImage
public extension UIView {
func convertToImage() -> UIImage {
return UIImage.drawFromView(view: self)
}
}
// MARK: - gradeView
public extension UIView {
func addGrade(alpha: CGFloat, color: UIColor = UIColor.black) -> UIView {
let gradeView = UIView.init()
gradeView.alpha = 0
self.addSubview(gradeView)
SPConstraintsAssistent.setEqualSizeConstraint(gradeView, superVuew: self)
SPConstraints.setEqualSize(gradeView, superVuew: self)
gradeView.alpha = alpha
gradeView.backgroundColor = color
return gradeView
}
}
// MARK: - shadow
extension UIView {
func setShadow(
@@ -249,7 +238,6 @@ extension UIView {
}
}
// MARK: - animation
extension UIView {
func addCornerRadiusAnimation(to: CGFloat, duration: CFTimeInterval) {
@@ -276,9 +264,12 @@ extension UIView {
self.isHidden = true
})
}
func removeAllAnimations() {
self.layer.removeAllAnimations()
}
}
// MARK: - corner radius
extension UIView {
func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
@@ -21,15 +21,16 @@
import UIKit
struct SPApp {
extension UIVisualEffectView {
static var udid: String? {
return UIDevice.current.identifierForVendor?.uuidString
convenience init(style: UIBlurEffect.Style) {
let effect = UIBlurEffect(style: style)
self.init(effect: effect)
}
static var displayName: String? {
return Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
convenience init(vibrancy style: UIBlurEffect.Style) {
let effect = UIBlurEffect(style: style)
let vibrancyEffect = UIVibrancyEffect(blurEffect: effect)
self.init(effect: vibrancyEffect)
}
private init() {}
}
@@ -21,7 +21,7 @@
import UIKit
class SPLayout {
struct SPLayout {
static func sizeWith(widthFactor: CGFloat, maxWidth: CGFloat, heightFactor: CGFloat, maxHeight: CGFloat, relativeSideFactor: CGFloat, from relativeSize: CGSize) -> CGSize {
@@ -39,4 +39,6 @@ class SPLayout {
}
return CGSize.init(width: prepareWidth, height: prepareHeight)
}
private init() {}
}
@@ -33,12 +33,6 @@ public extension String {
}
}
public extension Bool {
public static func random() -> Bool {
return arc4random_uniform(2) == 0
}
}
public extension Int {
public static func random(_ n: Int) -> Int {
return Int(arc4random_uniform(UInt32(n)))
@@ -40,5 +40,9 @@ public struct SPNativeStyleKit {
static let midGray = UIColor.init(hex: "C7C7CC")
static let gray = UIColor.init(hex: "8E8E93")
static let black = UIColor.init(hex: "000000")
private init() {}
}
private init() {}
}
@@ -63,53 +63,10 @@ public enum SPSeparatorInsetStyle {
case auto
}
@objc public enum SPPermissionType: Int {
case camera = 0
case photoLibrary = 1
case notification = 2
case microphone = 3
case calendar = 4
case contacts = 5
case reminders = 6
case speech = 7
case locationAlways = 8
case locationWhenInUse = 9
case locationWithBackground = 10
case mediaLibrary = 11
var name: String {
switch self {
case .camera:
return "Camera"
case .photoLibrary:
return "Photo Library"
case .notification:
return "Notification"
case .microphone:
return "Microphone"
case .calendar:
return "Calendar"
case .contacts:
return "Contacts"
case .reminders:
return "Reminders"
case .speech:
return "Speech"
case .locationAlways:
return "Location"
case .locationWhenInUse:
return "Location"
case .locationWithBackground:
return "Location"
case .mediaLibrary:
return "Media Library"
}
}
}
public enum SPNavigationTitleStyle {
case large
case small
case stork
}
public enum SPSystemApp {
@@ -26,7 +26,7 @@ class SPAppStoreActionButton: SPDownloadingButton {
var style: Style = .base {
didSet {
self.setTitleColorForNoramlAndHightlightedStates(color: self.baseColor)
self.setTitleColor(self.baseColor)
self.setTitle(self.titleLabel?.text, for: UIControl.State.normal)
switch self.style {
@@ -38,14 +38,14 @@ class SPAppStoreActionButton: SPDownloadingButton {
case .main:
self.backgroundColor = self.baseColor
self.layer.borderWidth = 0
self.setTitleColorForNoramlAndHightlightedStates(color: UIColor.white)
self.setTitleColor(UIColor.white)
self.titleLabel?.font = UIFont.system(type: .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.setTitleColorForNoramlAndHightlightedStates(color: UIColor.white)
self.setTitleColor(UIColor.white)
self.titleLabel?.font = UIFont.system(type: .Bold, size: 14)
self.contentEdgeInsets = UIEdgeInsets.init(top: 8, left: 15, bottom: 8, right: 15)
break
@@ -73,17 +73,7 @@ class SPAppStoreActionButton: SPDownloadingButton {
}
}
init() {
super.init(frame: CGRect.zero)
self.commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonInit()
}
private func commonInit() {
override func commonInit() {
self.style = .base
self.layer.masksToBounds = true
}
@@ -0,0 +1,70 @@
// 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 SPAppleMusicButton: SPButton {
var mode: Mode = .unselect {
didSet {
self.updateStyle(animated: false)
}
}
var selectColor: UIColor = UIColor.init(hex: "FD2D55") {
didSet {
self.updateStyle(animated: false)
}
}
var baseColor: UIColor = UIColor.init(hex: "F8F7FC") {
didSet {
self.updateStyle(animated: false)
}
}
override func commonInit() {
super.commonInit()
self.layer.cornerRadius = 8
self.titleLabel?.font = UIFont.system(type: .DemiBold, size: 15)
self.contentEdgeInsets = UIEdgeInsets.init(top: 12, left: 27, bottom: 12, right: 27)
self.mode = .unselect
}
private func updateStyle(animated: Bool) {
switch self.mode {
case .select:
self.backgroundColor = self.selectColor
self.setTitleColor(UIColor.white)
break
case .unselect:
self.backgroundColor = self.baseColor
self.setTitleColor(self.selectColor)
break
}
}
enum Mode {
case select
case unselect
}
}
@@ -21,35 +21,42 @@
import UIKit
extension UIWindow {
public class SPButton: UIButton {
static var key: UIWindow? {
return UIApplication.shared.keyWindow
var gradientView: SPGradientView? {
didSet {
self.gradientView?.isUserInteractionEnabled = false
if self.gradientView?.superview == nil {
if self.gradientView != nil {
self.insertSubview(self.gradientView!, at: 0)
}
}
}
}
static var topSafeArea: CGFloat {
var topSafeArea: CGFloat = 0
if let window = UIWindow.key {
if #available(iOS 11.0, *) {
topSafeArea = window.safeAreaInsets.top
}
} else {
topSafeArea = 0
var round: Bool = false {
didSet {
self.layoutSubviews()
}
return topSafeArea
}
static var bottomSafeArea: CGFloat {
var bottomSafeArea: CGFloat = 0
if let window = UIWindow.key {
if #available(iOS 11.0, *) {
bottomSafeArea = window.safeAreaInsets.bottom
}
} else {
bottomSafeArea = 0
init() {
super.init(frame: CGRect.zero)
self.commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonInit()
}
internal func commonInit() {}
override public func layoutSubviews() {
super.layoutSubviews()
self.gradientView?.setEqualsBoundsFromSuperview()
if self.round {
self.round()
}
return bottomSafeArea
}
}
@@ -0,0 +1,100 @@
// 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 SPDotButton: SPButton {
var customSideSize: CGFloat = 26 {
didSet {
self.sizeToFit()
}
}
var dotColor: UIColor = UIColor.white {
didSet {
for dotView in self.dotsView {
dotView.backgroundColor = self.dotColor
}
}
}
override var isHighlighted: Bool{
didSet{
if isHighlighted{
UIView.animate(withDuration: 0.1, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 1.0, options: [.curveEaseOut, .beginFromCurrentState], animations: {
for dotView in self.dotsView {
dotView.alpha = 0.35
}
}, completion: nil)
}else{
UIView.animate(withDuration: 0.35, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 1.0, options: [.curveEaseOut, .beginFromCurrentState], animations: {
for dotView in self.dotsView {
dotView.alpha = 1
}
}, completion: nil)
}
}
}
private var dotsView: [UIView] = []
override func commonInit() {
super.commonInit()
self.backgroundColor = UIColor.black.withAlphaComponent(0.2)
for _ in 0...2 {
let dotView = UIView()
dotView.isUserInteractionEnabled = false
dotView.backgroundColor = self.dotColor
self.dotsView.append(dotView)
self.addSubview(dotView)
}
}
override func sizeToFit() {
super.sizeToFit()
self.setWidth(self.customSideSize)
self.setHeight(self.customSideSize)
self.layoutSubviews()
}
override func layoutSubviews() {
super.layoutSubviews()
let space: CGFloat = 2
let sideSize: CGFloat = 4
let insest: CGFloat = (self.frame.width - (sideSize * 3) - (space * 2)) / 2
var currentXPosition: CGFloat = insest
for dotView in self.dotsView {
dotView.setWidth(sideSize)
dotView.setHeight(sideSize)
dotView.setYCenteringFromSuperview()
dotView.frame.origin.x = currentXPosition
dotView.round()
currentXPosition += (sideSize + space)
}
self.round()
}
}
@@ -21,7 +21,7 @@
import UIKit
class SPDownloadingButton: UIButton {
class SPDownloadingButton: SPButton {
let activityIndicatorView = UIActivityIndicatorView.init()
var isFrameRounded: Bool = false
@@ -21,31 +21,31 @@
import UIKit
class SPNativeOS11Button: SPDownloadingButton {
class SPNativeLargeButton: SPDownloadingButton {
override var isHighlighted: Bool {
didSet {
if isHighlighted {
self.backgroundColor = self.backgroundColor?.withAlphaComponent(0.7)
if self.gradientView == nil {
if isHighlighted {
self.backgroundColor = self.backgroundColor?.withAlphaComponent(0.7)
} else {
self.backgroundColor = self.backgroundColor?.withAlphaComponent(1)
}
} else {
self.backgroundColor = self.backgroundColor?.withAlphaComponent(1)
self.backgroundColor = self.backgroundColor?.withAlphaComponent(0)
if isHighlighted {
self.gradientView?.alpha = 0.7
} else {
self.gradientView?.alpha = 1
}
}
}
}
init() {
super.init(frame: CGRect.zero)
self.commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonInit()
}
private func commonInit() {
override func commonInit() {
super.commonInit()
self.titleLabel?.font = UIFont.system(type: UIFont.BoldType.DemiBold, size: 16)
self.setTitleColorForNoramlAndHightlightedStates(color: UIColor.white)
self.setTitleColor(UIColor.white)
self.backgroundColor = SPNativeStyleKit.Colors.blue
self.layer.masksToBounds = true
self.layer.cornerRadius = 8
@@ -61,5 +61,12 @@ class SPNativeOS11Button: SPDownloadingButton {
self.setWidth(width)
}
}
override func layoutSubviews() {
super.layoutSubviews()
self.gradientView?.setEqualsBoundsFromSuperview()
self.gradientView?.layer.cornerRadius = self.layer.cornerRadius
self.gradientView?.gradientLayer.cornerRadius = self.layer.cornerRadius
}
}
@@ -21,7 +21,7 @@
import UIKit
class SPSocialIconButton: UIButton {
class SPSocialButton: UIButton {
let iconView = SPSocialIconView.init()
var widthIconFactor: CGFloat = 0.5
@@ -76,5 +76,4 @@ class SPSystemIconButton: UIButton {
super.layoutSubviews()
self.iconView.setEqualsFrameFromBounds(self, withWidthFactor: self.widthIconFactor, withHeightFactor: self.heightIconFactor, withCentering: true)
}
}
@@ -59,7 +59,7 @@ class SPBaseTableViewController: SPStatusBarManagerTableViewController {
func updateLayout(with size: CGSize) {
let layoutIfShowKeyboard = {
let height = size.height - (self.keyboardSize?.height ?? 0) - self.topSafeArea
let height = size.height - (self.keyboardSize?.height ?? 0) - self.safeArea.top
self.emptyProposeView?.frame = CGRect.init(
x: 0, y: 0,
width: size.width * self.emptyProposeViewWidthFactor,
@@ -69,7 +69,7 @@ class SPBaseTableViewController: SPStatusBarManagerTableViewController {
}
let layoutIfNotShowKeyboard = {
let height = size.height - self.topSafeArea - self.bottomSafeArea
let height = size.height - self.safeArea.top - self.safeArea.bottom
self.emptyProposeView?.frame = CGRect.init(
x: 0, y: 0,
width: size.width * self.emptyProposeViewWidthFactor,
@@ -93,7 +93,7 @@ class SPBaseTableViewController: SPStatusBarManagerTableViewController {
} else {
self.activityIndicatorView.center = CGPoint.init(
x: size.width / 2,
y: (size.height - self.topSafeArea - self.bottomSafeArea) / 2
y: (size.height - self.safeArea.top - self.safeArea.bottom) / 2
)
}
}
@@ -299,4 +299,13 @@ class SPBaseTableViewController: SPStatusBarManagerTableViewController {
@objc func dismiss(sender: Any) {
self.dismiss()
}
func addHideButton(title: String) {
self.navigationItem.rightBarButtonItem = UIBarButtonItem.init(
title: title,
style: UIBarButtonItem.Style.done,
target: self,
action: #selector(self.dismiss(sender:))
)
}
}
@@ -59,14 +59,14 @@ public class SPBaseViewController: SPStatusBarManagerViewController {
func updateLayout(with size: CGSize) {
var contentHeight = size.height - self.topSafeArea
var contentHeight = size.height - self.safeArea.top
if self.isShowKeyboard {
contentHeight = contentHeight - (self.keyboardSize?.height ?? 0)
} else {
contentHeight = contentHeight - self.bottomSafeArea
contentHeight = contentHeight - self.safeArea.bottom
}
let centerYPosition = self.topSafeArea + (contentHeight / 2)
let centerYPosition = self.safeArea.top + (contentHeight / 2)
if self.activityIndicatorLayoutWithSafeArea {
self.activityIndicatorView.center = CGPoint.init(
@@ -88,7 +88,7 @@ public class SPBaseViewController: SPStatusBarManagerViewController {
self.emptyProposeView?.frame = CGRect.init(
x: 0, y: 0,
width: emptyProposeViewWidth,
height: (size.height - self.topSafeArea - self.bottomSafeArea) * self.emptyProposeViewHeightFactor
height: (size.height - self.safeArea.top - self.safeArea.bottom) * self.emptyProposeViewHeightFactor
)
self.emptyProposeView?.center = CGPoint.init(
x: size.width / 2,
@@ -103,6 +103,19 @@ public class SPBaseViewController: SPStatusBarManagerViewController {
}
}
func addHideButton(title: String) {
self.navigationItem.rightBarButtonItem = UIBarButtonItem.init(
title: title,
style: UIBarButtonItem.Style.done,
target: self,
action: #selector(self.dismiss(sender:))
)
}
@objc func dismiss(sender: Any) {
self.dismiss()
}
//MARK: - keyboard
var isShowKeyboard: Bool = false
var keyboardSize: CGSize? = nil
@@ -166,7 +166,7 @@ class SPConfirmActionViewController: UIViewController {
self.addSubview(self.label)
self.button.setTitle("Сancel", for: .normal)
self.button.setTitleColorForNoramlAndHightlightedStates(color: SPNativeStyleKit.Colors.blue)
self.button.setTitleColor(SPNativeStyleKit.Colors.blue)
self.button.titleLabel?.font = UIFont.system(type: .DemiBold, size: 16)
self.addSubview(self.button)
}
@@ -183,19 +183,6 @@ extension SPNativeTableViewController {
}
//MARK: - hide button
extension SPNativeTableViewController {
func addHideButton(title: String) {
self.navigationItem.rightBarButtonItem = UIBarButtonItem.init(
title: title,
style: UIBarButtonItem.Style.done,
target: self,
action: #selector(self.dismiss(sender:))
)
}
}
//MARK: - manage spaces
extension SPNativeTableViewController {
@@ -83,7 +83,7 @@ class SPProposeViewController: SPBaseViewController {
self.areaView.layoutSubviews()
self.areaView.sizeToFit()
self.areaView.frame.origin.x = self.space
self.areaView.frame.origin.y = self.view.frame.size.height - self.areaView.frame.height - (UIWindow.bottomSafeArea / 2) - self.space
self.areaView.frame.origin.y = self.view.frame.size.height - self.areaView.frame.height - (self.bottomSafeArea / 2) - self.space
}
@@ -95,7 +95,7 @@ class SPProposeViewController: SPBaseViewController {
SPAnimationSpring.animate(self.animationDuration, animations: {
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.6)
self.areaView.frame.origin.y = self.view.frame.size.height - self.areaView.frame.height - (UIWindow.bottomSafeArea / 2) - self.space
self.areaView.frame.origin.y = self.view.frame.size.height - self.areaView.frame.height - (self.bottomSafeArea / 2) - self.space
}, spring: 1,
velocity: 1,
options: .transitionCurlUp)
@@ -134,7 +134,7 @@ class SPProposeViewController: SPBaseViewController {
let returnAreaViewToPoint = {
SPAnimationSpring.animate(self.animationDuration, animations: {
self.areaView.frame.origin.y = self.view.frame.size.height - self.areaView.frame.height - (UIWindow.bottomSafeArea / 2) - self.space
self.areaView.frame.origin.y = self.view.frame.size.height - self.areaView.frame.height - (self.bottomSafeArea / 2) - self.space
}, spring: 1,
velocity: 1,
options: .transitionCurlDown,
@@ -173,7 +173,7 @@ class SPProposeViewController: SPBaseViewController {
let titleLabel = UILabel()
let subtitleLabel = UILabel()
let imageView = SPDownloadingImageView()
let button = SPNativeOS11Button()
let button = SPNativeLargeButton()
let closeButton = SPSystemIconButton(type: SPSystemIconType.close)
var imageSideSize: CGFloat = 160
@@ -206,7 +206,7 @@ class SPProposeViewController: SPBaseViewController {
self.addSubview(self.imageView)
self.button.titleLabel?.font = UIFont.system(type: UIFont.BoldType.Medium, size: 15)
self.button.setTitleColorForNoramlAndHightlightedStates(color: SPNativeStyleKit.Colors.black)
self.button.setTitleColor(SPNativeStyleKit.Colors.black)
self.button.backgroundColor = UIColor.init(hex: "D4D3DB")
self.addSubview(self.button)
@@ -266,4 +266,17 @@ class SPProposeViewController: SPBaseViewController {
var image: UIImage?
var complection: (_ isConfirmed: Bool)->()
}
var bottomSafeArea: CGFloat {
var bottomSafeArea: CGFloat = 0
if let window = UIApplication.shared.keyWindow {
if #available(iOS 11.0, *) {
bottomSafeArea = window.safeAreaInsets.bottom
}
} else {
bottomSafeArea = 0
}
return bottomSafeArea
}
}
@@ -28,7 +28,7 @@ class SPWelcomeViewController: SPBaseViewController {
let subtitleLabel = UILabel()
let descriptionLabel = UILabel()
let commentLabel = UILabel()
let button = SPNativeOS11Button()
let button = SPNativeLargeButton()
private var data: SPWelcomeData
private var views: [UIView] = []
@@ -134,7 +134,7 @@ class SPWelcomeViewController: SPBaseViewController {
let sideSpace: CGFloat = size.width * 0.112
self.imageView.frame = CGRect.init(x: sideSpace, y: self.topSafeArea + (size.height * 0.1), width: 84, height: 84)
self.imageView.frame = CGRect.init(x: sideSpace, y: self.safeArea.top + (size.height * 0.1), width: 84, height: 84)
let space: CGFloat = size.height * 0.025
@@ -148,7 +148,7 @@ class SPWelcomeViewController: SPBaseViewController {
self.descriptionLabel.sizeToFit()
self.button.sizeToFit()
self.button.frame.origin.y = size.height - self.bottomSafeArea - space - self.button.frame.height
self.button.frame.origin.y = size.height - self.safeArea.bottom - space - self.button.frame.height
self.button.setWidth(size.width - sideSpace * 2)
self.button.center.x = size.width / 2
@@ -214,5 +214,5 @@ struct SPWelcomeData {
var backgroundColor: UIColor = UIColor.white
var textColor: UIColor = UIColor.black
var statusBarStyle: SPStatusBar = .dark
var complection: (_ button: SPNativeOS11Button) -> () = { _ in }
var complection: (_ button: SPNativeLargeButton) -> () = { _ in }
}
@@ -0,0 +1,43 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
public class SPCollectionContainerCell<ContentView: UIView>: UICollectionViewCell {
let view = ContentView.init()
var currentIndexPath: IndexPath?
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.clear
self.addSubview(view)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func layoutSubviews() {
super.layoutSubviews()
self.view.setEqualsFrameFromBounds(self)
}
}
@@ -25,29 +25,20 @@ public class SPCollectionViewCell: UICollectionViewCell {
var currentIndexPath: IndexPath?
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonInit()
}
internal func commonInit() {}
public override func prepareForReuse() {
super.prepareForReuse()
self.currentIndexPath = nil
}
}
public class SPCollectionContainerCell<ContentView: UIView>: UICollectionViewCell {
let view = ContentView.init()
var currentIndexPath: IndexPath?
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.clear
self.addSubview(view)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func layoutSubviews() {
super.layoutSubviews()
self.view.setEqualsFrameFromBounds(self)
}
}
@@ -27,7 +27,7 @@ class SPImageCollectionViewCell: SPCollectionContainerCell<SPDownloadingImageVie
super.init(frame: frame)
self.view.layer.cornerRadius = 10
self.view.contentMode = .scaleAspectFill
self.view.setNativeStyle()
self.view.setNative()
}
required public init?(coder aDecoder: NSCoder) {
@@ -37,6 +37,6 @@ class SPImageCollectionViewCell: SPCollectionContainerCell<SPDownloadingImageVie
override func prepareForReuse() {
super.prepareForReuse()
self.view.contentMode = .scaleAspectFill
self.view.setLoadingMode()
self.view.startLoading()
}
}
@@ -44,18 +44,7 @@ class SPMengTransformCollectionViewCell: SPCollectionViewCell {
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonInit()
}
private func commonInit() {
override func commonInit() {
shadowContainerView.backgroundColor = UIColor.white
shadowContainerView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(shadowContainerView)
@@ -41,7 +41,7 @@ public class SPCollectionView: UICollectionView {
commonInit()
}
fileprivate func commonInit() {
internal func commonInit() {
self.layout.scrollDirection = .vertical
self.backgroundColor = UIColor.clear
self.collectionViewLayout = self.layout
@@ -46,17 +46,7 @@ class SPMengTransformCollectionView: SPCollectionView {
var withParalax: Bool = true
static var recomendedHeight: CGFloat = 310
override init() {
super.init()
self.commonInit()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonInit()
}
private func commonInit() {
override func commonInit() {
self.layout.scrollDirection = .horizontal
self.layout.cellSideRatio = 1.23
@@ -186,7 +176,7 @@ extension SPMengTransformCollectionView: UICollectionViewDelegate {
let indexPath = self.indexPath(for: cell)!
let attributes = self.layoutAttributesForItem(at: indexPath)!
if let rootController = SPRootViewController.controller {
if let rootController = SPApp.rootController {
let cellFrame = self.convert(attributes.frame, to: rootController.view)
if self.withParalax {

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