PartialScreenPresentationController.swift 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import Foundation
  2. import UIKit
  3. class PartialScreenPresentationController: UIPresentationController {
  4. let blurEffectView: UIVisualEffectView
  5. private var direction: CGFloat = 0
  6. lazy var tapGestureRecognizer: UITapGestureRecognizer = {
  7. return UITapGestureRecognizer(target: self, action: #selector(dismiss))
  8. }()
  9. lazy var panGestureRecognizer: UIPanGestureRecognizer = {
  10. return UIPanGestureRecognizer(target: self, action: #selector(didPan(pan:)))
  11. }()
  12. @objc func dismiss() {
  13. self.presentedViewController.dismiss(animated: true, completion: nil)
  14. }
  15. @objc func didPan(pan: UIPanGestureRecognizer) {
  16. guard let view = pan.view, let superView = view.superview,
  17. let presented = presentedView, let container = containerView else { return }
  18. let location = pan.translation(in: superView)
  19. let velocity = pan.velocity(in: superView)
  20. let maxPresentedY = container.frame.height / 2
  21. switch pan.state {
  22. case .changed:
  23. if location.y < 0 {
  24. break
  25. }
  26. presented.frame.origin.y = maxPresentedY + location.y
  27. case .ended:
  28. if velocity.y > 100 && location.y > 0 {
  29. presentedViewController.dismiss(animated: true, completion: nil)
  30. } else {
  31. let offset = maxPresentedY + (maxPresentedY / 3)
  32. switch presented.frame.origin.y {
  33. case 0...offset:
  34. UIView.animate(withDuration: 0.3, delay: 0.0, options: .curveEaseInOut, animations: {
  35. presented.frame.origin.y = maxPresentedY
  36. })
  37. default:
  38. presentedViewController.dismiss(animated: true, completion: nil)
  39. }
  40. }
  41. default:
  42. break
  43. }
  44. }
  45. override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
  46. let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.dark)
  47. blurEffectView = UIVisualEffectView(effect: blurEffect)
  48. super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
  49. blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  50. blurEffectView.isUserInteractionEnabled = true
  51. blurEffectView.addGestureRecognizer(tapGestureRecognizer)
  52. presentedViewController.view.addGestureRecognizer(panGestureRecognizer)
  53. }
  54. override var frameOfPresentedViewInContainerView: CGRect {
  55. guard let containerView = self.containerView else {
  56. return CGRect()
  57. }
  58. return CGRect(origin: CGPoint(x: 0,
  59. y: containerView.frame.height / 2),
  60. size: CGSize(width: containerView.frame.width,
  61. height: containerView.frame.height / 2))
  62. }
  63. override func dismissalTransitionWillBegin() {
  64. self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { _ in
  65. self.blurEffectView.alpha = 0
  66. }, completion: { _ in
  67. self.blurEffectView.removeFromSuperview()
  68. })
  69. }
  70. override func presentationTransitionWillBegin() {
  71. self.blurEffectView.alpha = 0
  72. self.containerView?.addSubview(blurEffectView)
  73. self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { _ in
  74. self.blurEffectView.alpha = 1
  75. })
  76. }
  77. override func containerViewWillLayoutSubviews() {
  78. super.containerViewWillLayoutSubviews()
  79. presentedView!.layer.masksToBounds = true
  80. presentedView!.layer.cornerRadius = 10
  81. }
  82. override func containerViewDidLayoutSubviews() {
  83. super.containerViewDidLayoutSubviews()
  84. self.presentedView?.frame = frameOfPresentedViewInContainerView
  85. blurEffectView.frame = containerView!.bounds
  86. }
  87. }
  88. final class PartialScreenModalTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
  89. init(from presented: UIViewController, to presenting: UIViewController) {
  90. super.init()
  91. }
  92. func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
  93. return PartialScreenPresentationController(presentedViewController: presented, presenting: presenting)
  94. }
  95. }